Migrate OpaqueHiddenType, E0282, E0283, E0284, E0698

This commit is contained in:
Nikita Tomashevich 2022-08-21 20:56:00 +03:00
parent 1cff564203
commit 313d474b35
No known key found for this signature in database
GPG key ID: B29791D4D878E345
7 changed files with 440 additions and 130 deletions

View file

@ -3983,6 +3983,7 @@ dependencies = [
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_target",
"smallvec",

View file

@ -0,0 +1,65 @@
infer_opaque_hidden_type =
opaque type's hidden type cannot be another opaque type from the same scope
.label = one of the two opaque types used here has to be outside its defining scope
.opaque_type = opaque type whose hidden type is being assigned
.hidden_type = opaque type being used as hidden type
infer_type_annotations_needed = {$source_kind ->
[closure] type annotations needed for the closure `{$source_name}`
[normal] type annotations needed for `{$source_name}`
*[other] type annotations needed
}
.label = type must be known at this point
infer_label_bad = {$bad_kind ->
*[other] cannot infer type
[more_info] cannot infer {$prefix_kind ->
*[type] type for {$prefix}
[const_with_param] the value of const parameter
[const] the value of the constant
} `{$name}`{$has_parent ->
[true] {" "}declared on the {$parent_prefix} `{$parent_name}`
*[false] {""}
}
}
infer_source_kind_subdiag_let = {$kind ->
[with_pattern] consider giving `{$name}` an explicit type
[closure] consider giving this closure parameter an explicit type
*[other] consider giving this pattern a type
}{$x_kind ->
[has_name] , where the {$prefix_kind ->
*[type] type for {$prefix}
[const_with_param] the value of const parameter
[const] the value of the constant
} `{$arg_name}` is specified
[underscore] , where the placeholders `_` are specified
*[empty] {""}
}
infer_source_kind_subdiag_generic_label =
cannot infer {$is_type ->
[true] type
*[false] the value
} of the {$is_type ->
[true] type
*[false] const
} {$parent_exists ->
[true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
*[false] parameter {$param_name}
}
infer_source_kind_subdiag_generic_suggestion =
consider specifying the generic {$arg_count ->
[one] argument
*[other] arguments
}
infer_source_kind_fully_qualified =
try using a fully qualified path to specify the expected types
infer_source_kind_closure_return =
try giving this closure an explicit return type
infer_need_type_info_in_generator =
type inside {$generator_kind} must be known in this context

View file

@ -38,6 +38,7 @@
const_eval => "../locales/en-US/const_eval.ftl",
expand => "../locales/en-US/expand.ftl",
interface => "../locales/en-US/interface.ftl",
infer => "../locales/en-US/infer.ftl",
lint => "../locales/en-US/lint.ftl",
parser => "../locales/en-US/parser.ftl",
passes => "../locales/en-US/passes.ftl",

View file

@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }

View file

@ -0,0 +1,187 @@
use rustc_errors::{fluent, AddSubdiagnostic};
use rustc_hir::FnRetTy;
use rustc_macros::SessionDiagnostic;
use rustc_span::{BytePos, Span};
#[derive(SessionDiagnostic)]
#[diag(infer::opaque_hidden_type)]
pub struct OpaqueHiddenTypeDiag {
#[primary_span]
#[label]
pub span: Span,
#[note(infer::opaque_type)]
pub opaque_type: Span,
#[note(infer::hidden_type)]
pub hidden_type: Span,
}
#[derive(SessionDiagnostic)]
#[diag(infer::type_annotations_needed, code = "E0282")]
pub struct AnnotationRequired<'a> {
#[primary_span]
pub span: Span,
pub source_kind: &'static str,
pub source_name: &'a str,
#[label]
pub failure_span: Option<Span>,
#[subdiagnostic]
pub bad_label: Option<InferenceBadError<'a>>,
#[subdiagnostic]
pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
#[subdiagnostic]
pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
}
// Copy of `AnnotationRequired` for E0283
#[derive(SessionDiagnostic)]
#[diag(infer::type_annotations_needed, code = "E0283")]
pub struct AmbigousImpl<'a> {
#[primary_span]
pub span: Span,
pub source_kind: &'static str,
pub source_name: &'a str,
#[label]
pub failure_span: Option<Span>,
#[subdiagnostic]
pub bad_label: Option<InferenceBadError<'a>>,
#[subdiagnostic]
pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
#[subdiagnostic]
pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
}
// Copy of `AnnotationRequired` for E0284
#[derive(SessionDiagnostic)]
#[diag(infer::type_annotations_needed, code = "E0284")]
pub struct AmbigousReturn<'a> {
#[primary_span]
pub span: Span,
pub source_kind: &'static str,
pub source_name: &'a str,
#[label]
pub failure_span: Option<Span>,
#[subdiagnostic]
pub bad_label: Option<InferenceBadError<'a>>,
#[subdiagnostic]
pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
#[subdiagnostic]
pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
}
#[derive(SessionDiagnostic)]
#[diag(infer::need_type_info_in_generator, code = "E0698")]
pub struct NeedTypeInfoInGenerator<'a> {
#[primary_span]
pub span: Span,
pub generator_kind: String,
#[subdiagnostic]
pub bad_label: InferenceBadError<'a>,
}
// Used when a better one isn't available
#[derive(SessionSubdiagnostic)]
#[label(infer::label_bad)]
pub struct InferenceBadError<'a> {
#[primary_span]
pub span: Span,
pub bad_kind: &'static str,
pub prefix_kind: &'static str,
pub has_parent: bool,
pub prefix: &'a str,
pub parent_prefix: &'a str,
pub parent_name: String,
pub name: String,
}
#[derive(SessionSubdiagnostic)]
pub enum SourceKindSubdiag<'a> {
#[suggestion_verbose(
infer::source_kind_subdiag_let,
code = ": {type_name}",
applicability = "has-placeholders"
)]
LetLike {
#[primary_span]
span: Span,
name: String,
type_name: String,
kind: &'static str,
x_kind: &'static str,
prefix_kind: &'static str,
prefix: &'a str,
arg_name: String,
},
#[label(infer::source_kind_subdiag_generic_label)]
GenericLabel {
#[primary_span]
span: Span,
is_type: bool,
param_name: String,
parent_exists: bool,
parent_prefix: String,
parent_name: String,
},
#[suggestion_verbose(
infer::source_kind_subdiag_generic_suggestion,
code = "::<{args}>",
applicability = "has-placeholders"
)]
GenericSuggestion {
#[primary_span]
span: Span,
arg_count: usize,
args: String,
},
}
// Has to be implemented manually because multipart suggestions are not supported by the derive macro.
// Would be a part of `SourceKindSubdiag` otherwise.
pub enum SourceKindMultiSuggestion<'a> {
FullyQualified {
span: Span,
def_path: String,
adjustment: &'a str,
successor: (&'a str, BytePos),
},
ClosureReturn {
ty_info: String,
data: &'a FnRetTy<'a>,
should_wrap_expr: Option<Span>,
},
}
impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> {
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
match self {
Self::FullyQualified { span, def_path, adjustment, successor } => {
let suggestion = vec![
(span.shrink_to_lo(), format!("{def_path}({adjustment}")),
(span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
];
diag.multipart_suggestion_verbose(
fluent::infer::source_kind_fully_qualified,
suggestion,
rustc_errors::Applicability::HasPlaceholders,
);
}
Self::ClosureReturn { ty_info, data, should_wrap_expr } => {
let (arrow, post) = match data {
FnRetTy::DefaultReturn(_) => ("-> ", " "),
_ => ("", ""),
};
let suggestion = match should_wrap_expr {
Some(end_span) => vec![
(data.span(), format!("{}{}{}{{ ", arrow, ty_info, post)),
(end_span, " }".to_string()),
],
None => vec![(data.span(), format!("{}{}{}", arrow, ty_info, post))],
};
diag.multipart_suggestion_verbose(
fluent::infer::source_kind_closure_return,
suggestion,
rustc_errors::Applicability::HasPlaceholders,
);
}
}
}
}

View file

@ -1,6 +1,10 @@
use crate::errors::{
AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
SourceKindMultiSuggestion, SourceKindSubdiag,
};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::InferCtxt;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{CtorOf, DefKind, Namespace};
@ -14,6 +18,7 @@
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
use rustc_middle::ty::{self, DefIdTree, InferConst};
use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
use rustc_session::SessionDiagnostic;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{BytePos, Span};
use std::borrow::Cow;
@ -66,32 +71,42 @@ pub enum UnderspecifiedArgKind {
}
impl InferenceDiagnosticsData {
/// Generate a label for a generic argument which can't be inferred. When not
/// much is known about the argument, `use_diag` may be used to describe the
/// labeled value.
fn cannot_infer_msg(&self) -> String {
if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
return "cannot infer type".to_string();
}
let suffix = match &self.parent {
Some(parent) => parent.suffix_string(),
None => String::new(),
};
// For example: "cannot infer type for type parameter `T`"
format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
fn can_add_more_info(&self) -> bool {
!(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
}
fn where_x_is_specified(&self, in_type: Ty<'_>) -> String {
fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
if in_type.is_ty_infer() {
String::new()
"empty"
} else if self.name == "_" {
// FIXME: Consider specializing this message if there is a single `_`
// in the type.
", where the placeholders `_` are specified".to_string()
"underscore"
} else {
format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name)
"has_name"
}
}
/// Generate a label for a generic argument which can't be inferred. When not
/// much is known about the argument, `use_diag` may be used to describe the
/// labeled value.
fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> {
let has_parent = self.parent.is_some();
let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" };
let (parent_prefix, parent_name) = self
.parent
.as_ref()
.map(|parent| (parent.prefix, parent.name.clone()))
.unwrap_or_default();
InferenceBadError {
span,
bad_kind,
prefix_kind: self.kind.prefix_kind(),
prefix: self.kind.try_get_prefix().unwrap_or_default(),
name: self.name.clone(),
has_parent,
parent_prefix,
parent_name,
}
}
}
@ -113,18 +128,20 @@ fn for_parent_def_id(
fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
Self::for_parent_def_id(tcx, tcx.parent(def_id))
}
fn suffix_string(&self) -> String {
format!(" declared on the {} `{}`", self.prefix, self.name)
}
}
impl UnderspecifiedArgKind {
fn prefix_string(&self) -> Cow<'static, str> {
fn prefix_kind(&self) -> &'static str {
match self {
Self::Type { prefix } => format!("type for {}", prefix).into(),
Self::Const { is_parameter: true } => "the value of const parameter".into(),
Self::Const { is_parameter: false } => "the value of the constant".into(),
Self::Type { .. } => "type",
Self::Const { is_parameter: true } => "const_with_param",
Self::Const { is_parameter: false } => "const",
}
}
fn try_get_prefix(&self) -> Option<&str> {
match self {
Self::Type { prefix } => Some(prefix.as_ref()),
Self::Const { .. } => None,
}
}
}
@ -303,11 +320,44 @@ fn bad_inference_failure_err(
arg_data: InferenceDiagnosticsData,
error_code: TypeAnnotationNeeded,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let error_code = error_code.into();
let mut err =
self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code);
err.span_label(span, arg_data.cannot_infer_msg());
err
let source_kind = "other";
let source_name = "";
let failure_span = None;
let infer_subdiags = Vec::new();
let multi_suggestions = Vec::new();
let bad_label = Some(arg_data.make_bad_error(span));
match error_code {
TypeAnnotationNeeded::E0282 => AnnotationRequired {
span,
source_kind,
source_name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
TypeAnnotationNeeded::E0283 => AmbigousImpl {
span,
source_kind,
source_name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
TypeAnnotationNeeded::E0284 => AmbigousReturn {
span,
source_kind,
source_name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
}
}
pub fn emit_inference_failure_err(
@ -340,48 +390,39 @@ pub fn emit_inference_failure_err(
return self.bad_inference_failure_err(failure_span, arg_data, error_code)
};
let error_code = error_code.into();
let mut err = self.tcx.sess.struct_span_err_with_code(
span,
&format!("type annotations needed{}", kind.ty_msg(self)),
error_code,
);
if should_label_span && !failure_span.overlaps(span) {
err.span_label(failure_span, "type must be known at this point");
}
let (source_kind, name) = kind.ty_localized_msg(self);
let failure_span = if should_label_span && !failure_span.overlaps(span) {
Some(failure_span)
} else {
None
};
let mut infer_subdiags = Vec::new();
let mut multi_suggestions = Vec::new();
match kind {
InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
let suggestion_msg = if let Some(name) = pattern_name {
format!(
"consider giving `{}` an explicit type{}",
name,
arg_data.where_x_is_specified(ty)
)
} else {
format!(
"consider giving this pattern a type{}",
arg_data.where_x_is_specified(ty)
)
};
err.span_suggestion_verbose(
insert_span,
&suggestion_msg,
format!(": {}", ty_to_string(self, ty)),
Applicability::HasPlaceholders,
);
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
x_kind: arg_data.where_x_is_kind(ty),
prefix_kind: arg_data.kind.prefix_kind(),
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
type_name: ty_to_string(self, ty),
});
}
InferSourceKind::ClosureArg { insert_span, ty } => {
err.span_suggestion_verbose(
insert_span,
&format!(
"consider giving this closure parameter an explicit type{}",
arg_data.where_x_is_specified(ty)
),
format!(": {}", ty_to_string(self, ty)),
Applicability::HasPlaceholders,
);
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: String::new(),
x_kind: arg_data.where_x_is_kind(ty),
prefix_kind: arg_data.kind.prefix_kind(),
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
kind: "closure",
type_name: ty_to_string(self, ty),
});
}
InferSourceKind::GenericArg {
insert_span,
@ -393,19 +434,20 @@ pub fn emit_inference_failure_err(
let generics = self.tcx.generics_of(generics_def_id);
let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
let cannot_infer_msg = format!(
"cannot infer {} of the {} parameter `{}`{}",
if is_type { "type" } else { "the value" },
if is_type { "type" } else { "const" },
generics.params[argument_index].name,
// We use the `generics_def_id` here, as even when suggesting `None::<T>`,
// the type parameter `T` was still declared on the enum, not on the
// variant.
let (parent_exists, parent_prefix, parent_name) =
InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
.map_or(String::new(), |parent| parent.suffix_string()),
);
.map_or((false, String::new(), String::new()), |parent| {
(true, parent.prefix.to_string(), parent.name)
});
err.span_label(span, cannot_infer_msg);
infer_subdiags.push(SourceKindSubdiag::GenericLabel {
span,
is_type,
param_name: generics.params[argument_index].name.to_string(),
parent_exists,
parent_prefix,
parent_name,
});
let args = fmt_printer(self, Namespace::TypeNS)
.comma_sep(generic_args.iter().copied().map(|arg| {
@ -435,15 +477,11 @@ pub fn emit_inference_failure_err(
.unwrap()
.into_buffer();
err.span_suggestion_verbose(
insert_span,
&format!(
"consider specifying the generic argument{}",
pluralize!(generic_args.len()),
),
format!("::<{}>", args),
Applicability::HasPlaceholders,
);
infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
span: insert_span,
arg_count: generic_args.len(),
args,
});
}
InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => {
let printer = fmt_printer(self, Namespace::ValueNS);
@ -468,37 +506,54 @@ pub fn emit_inference_failure_err(
_ => "",
};
let suggestion = vec![
(receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")),
(receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
];
err.multipart_suggestion_verbose(
"try using a fully qualified path to specify the expected types",
suggestion,
Applicability::HasPlaceholders,
);
multi_suggestions.push(SourceKindMultiSuggestion::FullyQualified {
span: receiver.span,
def_path,
adjustment,
successor,
});
}
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
let ret = ty_to_string(self, ty);
let (arrow, post) = match data {
FnRetTy::DefaultReturn(_) => ("-> ", " "),
_ => ("", ""),
};
let suggestion = match should_wrap_expr {
Some(end_span) => vec![
(data.span(), format!("{}{}{}{{ ", arrow, ret, post)),
(end_span, " }".to_string()),
],
None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))],
};
err.multipart_suggestion_verbose(
"try giving this closure an explicit return type",
suggestion,
Applicability::HasPlaceholders,
);
let ty_info = ty_to_string(self, ty);
multi_suggestions.push(SourceKindMultiSuggestion::ClosureReturn {
ty_info,
data,
should_wrap_expr,
});
}
}
err
match error_code {
TypeAnnotationNeeded::E0282 => AnnotationRequired {
span,
source_kind,
source_name: &name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label: None,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
TypeAnnotationNeeded::E0283 => AmbigousImpl {
span,
source_kind,
source_name: &name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label: None,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
TypeAnnotationNeeded::E0284 => AmbigousReturn {
span,
source_kind,
source_name: &name,
failure_span,
infer_subdiags,
multi_suggestions,
bad_label: None,
}
.into_diagnostic(&self.tcx.sess.parse_sess),
}
}
pub fn need_type_info_err_in_generator(
@ -510,15 +565,12 @@ pub fn need_type_info_err_in_generator(
let ty = self.resolve_vars_if_possible(ty);
let data = self.extract_inference_diagnostics_data(ty.into(), None);
let mut err = struct_span_err!(
self.tcx.sess,
NeedTypeInfoInGenerator {
bad_label: data.make_bad_error(span),
span,
E0698,
"type inside {} must be known in this context",
kind,
);
err.span_label(span, data.cannot_infer_msg());
err
generator_kind: kind.to_string(),
}
.into_diagnostic(&self.tcx.sess.parse_sess)
}
}
@ -579,22 +631,22 @@ fn from_expansion(&self) -> bool {
}
impl<'tcx> InferSourceKind<'tcx> {
fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
fn ty_localized_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> (&'static str, String) {
match *self {
InferSourceKind::LetBinding { ty, .. }
| InferSourceKind::ClosureArg { ty, .. }
| InferSourceKind::ClosureReturn { ty, .. } => {
if ty.is_closure() {
format!(" for the closure `{}`", closure_as_fn_str(infcx, ty))
("closure", closure_as_fn_str(infcx, ty))
} else if !ty.is_ty_infer() {
format!(" for `{}`", ty_to_string(infcx, ty))
("normal", ty_to_string(infcx, ty))
} else {
String::new()
("other", String::new())
}
}
// FIXME: We should be able to add some additional info here.
InferSourceKind::GenericArg { .. }
| InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(),
| InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()),
}
}
}

View file

@ -23,6 +23,8 @@
#![feature(never_type)]
#![feature(try_blocks)]
#![recursion_limit = "512"] // For rustdoc
// #![deny(rustc::untranslatable_diagnostic)]
// #![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate rustc_macros;
@ -34,5 +36,6 @@
#[macro_use]
extern crate rustc_middle;
mod errors;
pub mod infer;
pub mod traits;