blob: 23234c208069030c5f2de16fbdb6b4dfb034df0a [file] [log] [blame]
use rustc_ast::Path;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic,
EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg,
};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt};
use rustc_span::{BytePos, Ident, Span, Symbol, kw};
use crate::error_reporting::infer::ObligationCauseAsDiagArg;
use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind;
use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted;
pub mod note_and_explain;
#[derive(Diagnostic)]
#[diag("unable to construct a constant value for the unevaluated constant {$unevaluated}")]
pub struct UnableToConstructConstantValue<'a> {
#[primary_span]
pub span: Span,
pub unevaluated: ty::UnevaluatedConst<'a>,
}
#[derive(Diagnostic)]
pub enum InvalidOnClause {
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
Empty {
#[primary_span]
#[label("empty `on`-clause here")]
span: Span,
},
#[diag("expected a single predicate in `not(..)`", code = E0232)]
ExpectedOnePredInNot {
#[primary_span]
#[label("unexpected quantity of predicates here")]
span: Span,
},
#[diag("literals inside `on`-clauses are not supported", code = E0232)]
UnsupportedLiteral {
#[primary_span]
#[label("unexpected literal here")]
span: Span,
},
#[diag("expected an identifier inside this `on`-clause", code = E0232)]
ExpectedIdentifier {
#[primary_span]
#[label("expected an identifier here, not `{$path}`")]
span: Span,
path: Path,
},
#[diag("this predicate is invalid", code = E0232)]
InvalidPredicate {
#[primary_span]
#[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")]
span: Span,
invalid_pred: Symbol,
},
#[diag("invalid flag in `on`-clause", code = E0232)]
InvalidFlag {
#[primary_span]
#[label(
"expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`"
)]
span: Span,
invalid_flag: Symbol,
},
#[diag("invalid name in `on`-clause", code = E0232)]
InvalidName {
#[primary_span]
#[label(
"expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`"
)]
span: Span,
invalid_name: Symbol,
},
}
#[derive(Diagnostic)]
#[diag("this attribute must have a value", code = E0232)]
#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")]
pub struct NoValueInOnUnimplemented {
#[primary_span]
#[label("expected value here")]
pub span: Span,
}
pub struct NegativePositiveConflict<'tcx> {
pub impl_span: Span,
pub trait_desc: ty::TraitRef<'tcx>,
pub self_ty: Option<Ty<'tcx>>,
pub negative_impl_span: Result<Span, Symbol>,
pub positive_impl_span: Result<Span, Symbol>,
}
impl<G: EmissionGuarantee> Diagnostic<'_, G> for NegativePositiveConflict<'_> {
#[track_caller]
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
let mut diag = Diag::new(
dcx,
level,
msg!(
"found both positive and negative implementation of trait `{$trait_desc}`{$self_desc ->
[none] {\"\"}
*[default] {\" \"}for type `{$self_desc}`
}:"
),
);
diag.arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
diag.arg("self_desc", self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()));
diag.span(self.impl_span);
diag.code(E0751);
match self.negative_impl_span {
Ok(span) => {
diag.span_label(span, msg!("negative implementation here"));
}
Err(cname) => {
diag.note(msg!("negative implementation in crate `{$negative_impl_cname}`"));
diag.arg("negative_impl_cname", cname.to_string());
}
}
match self.positive_impl_span {
Ok(span) => {
diag.span_label(span, msg!("positive implementation here"));
}
Err(cname) => {
diag.note(msg!("positive implementation in crate `{$positive_impl_cname}`"));
diag.arg("positive_impl_cname", cname.to_string());
}
}
diag
}
}
#[derive(Diagnostic)]
#[diag("overflow evaluating associated type `{$ty}`")]
pub struct InherentProjectionNormalizationOverflow {
#[primary_span]
pub span: Span,
pub ty: String,
}
pub enum AdjustSignatureBorrow {
Borrow { to_borrow: Vec<(Span, String)> },
RemoveBorrow { remove_borrow: Vec<(Span, String)> },
}
impl Subdiagnostic for AdjustSignatureBorrow {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self {
AdjustSignatureBorrow::Borrow { to_borrow } => {
diag.arg("borrow_len", to_borrow.len());
diag.multipart_suggestion(
msg!(
"consider adjusting the signature so it borrows its {$borrow_len ->
[one] argument
*[other] arguments
}"
),
to_borrow,
Applicability::MaybeIncorrect,
);
}
AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
diag.arg("remove_borrow_len", remove_borrow.len());
diag.multipart_suggestion(
msg!(
"consider adjusting the signature so it does not borrow its {$remove_borrow_len ->
[one] argument
*[other] arguments
}"
),
remove_borrow,
Applicability::MaybeIncorrect,
);
}
}
}
}
#[derive(Diagnostic)]
#[diag("expected a closure that implements the `{$trait_prefix}{$expected}` trait, but this closure only implements `{$trait_prefix}{$found}`", code = E0525)]
pub struct ClosureKindMismatch {
#[primary_span]
#[label("this closure implements `{$trait_prefix}{$found}`, not `{$trait_prefix}{$expected}`")]
pub closure_span: Span,
pub expected: ClosureKind,
pub found: ClosureKind,
#[label("the requirement to implement `{$trait_prefix}{$expected}` derives from here")]
pub cause_span: Span,
pub trait_prefix: &'static str,
#[subdiagnostic]
pub fn_once_label: Option<ClosureFnOnceLabel>,
#[subdiagnostic]
pub fn_mut_label: Option<ClosureFnMutLabel>,
}
#[derive(Subdiagnostic)]
#[label(
"closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment"
)]
pub struct ClosureFnOnceLabel {
#[primary_span]
pub span: Span,
pub place: String,
}
#[derive(Subdiagnostic)]
#[label("closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here")]
pub struct ClosureFnMutLabel {
#[primary_span]
pub span: Span,
pub place: String,
}
#[derive(Diagnostic)]
#[diag(
"{$coro_kind}closure does not implement `{$kind}` because it captures state from its environment"
)]
pub(crate) struct CoroClosureNotFn {
#[primary_span]
pub span: Span,
pub kind: &'static str,
pub coro_kind: String,
}
#[derive(Diagnostic)]
#[diag("{$source_kind ->
[closure] type annotations needed for the closure `{$source_name}`
[normal] type annotations needed for `{$source_name}`
*[other] 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("type must be known at this point")]
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(Diagnostic)]
#[diag("{$source_kind ->
[closure] type annotations needed for the closure `{$source_name}`
[normal] type annotations needed for `{$source_name}`
*[other] type annotations needed
}", code = E0283)]
pub struct AmbiguousImpl<'a> {
#[primary_span]
pub span: Span,
pub source_kind: &'static str,
pub source_name: &'a str,
#[label("type must be known at this point")]
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(Diagnostic)]
#[diag("{$source_kind ->
[closure] type annotations needed for the closure `{$source_name}`
[normal] type annotations needed for `{$source_name}`
*[other] type annotations needed
}", code = E0284)]
pub struct AmbiguousReturn<'a> {
#[primary_span]
pub span: Span,
pub source_kind: &'static str,
pub source_name: &'a str,
#[label("type must be known at this point")]
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>>,
}
// Used when a better one isn't available
#[derive(Subdiagnostic)]
#[label(
"{$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] {\"\"}
}
}"
)]
pub struct InferenceBadError<'a> {
#[primary_span]
pub span: Span,
pub bad_kind: &'static str,
pub prefix_kind: UnderspecifiedArgKind,
pub has_parent: bool,
pub prefix: &'a str,
pub parent_prefix: &'a str,
pub parent_name: String,
pub name: String,
}
#[derive(Subdiagnostic)]
pub enum SourceKindSubdiag<'a> {
#[suggestion(
"{$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] value of const parameter
[const] value of the constant
} `{$arg_name}` is specified
[underscore] , where the placeholders `_` are specified
*[empty] {\"\"}
}",
style = "verbose",
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: UnderspecifiedArgKind,
prefix: &'a str,
arg_name: String,
},
#[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}
}"
)]
GenericLabel {
#[primary_span]
span: Span,
is_type: bool,
param_name: String,
parent_exists: bool,
parent_prefix: String,
parent_name: String,
},
#[suggestion(
"consider specifying the generic {$arg_count ->
[one] argument
*[other] arguments
}",
style = "verbose",
code = "::<{args}>",
applicability = "has-placeholders"
)]
GenericSuggestion {
#[primary_span]
span: Span,
arg_count: usize,
args: String,
},
}
#[derive(Subdiagnostic)]
pub enum SourceKindMultiSuggestion<'a> {
#[multipart_suggestion(
"try using a fully qualified path to specify the expected types",
style = "verbose",
applicability = "has-placeholders"
)]
FullyQualified {
#[suggestion_part(code = "{def_path}({adjustment}")]
span_lo: Span,
#[suggestion_part(code = "{successor_pos}")]
span_hi: Span,
def_path: String,
adjustment: &'a str,
successor_pos: &'a str,
},
#[multipart_suggestion(
"try giving this closure an explicit return type",
style = "verbose",
applicability = "has-placeholders"
)]
ClosureReturn {
#[suggestion_part(code = "{start_span_code}")]
start_span: Span,
start_span_code: String,
#[suggestion_part(code = " }}")]
end_span: Option<Span>,
},
}
impl<'a> SourceKindMultiSuggestion<'a> {
pub fn new_fully_qualified(
span: Span,
def_path: String,
adjustment: &'a str,
successor: (&'a str, BytePos),
) -> Self {
Self::FullyQualified {
span_lo: span.shrink_to_lo(),
span_hi: span.shrink_to_hi().with_hi(successor.1),
def_path,
adjustment,
successor_pos: successor.0,
}
}
pub fn new_closure_return(
ty_info: String,
data: &'a FnRetTy<'a>,
should_wrap_expr: Option<Span>,
) -> Self {
let arrow = match data {
FnRetTy::DefaultReturn(_) => " -> ",
_ => "",
};
let (start_span, start_span_code, end_span) = match should_wrap_expr {
Some(end_span) => (data.span(), format!("{arrow}{ty_info} {{"), Some(end_span)),
None => (data.span(), format!("{arrow}{ty_info}"), None),
};
Self::ClosureReturn { start_span, start_span_code, end_span }
}
}
pub enum RegionOriginNote<'a> {
Plain {
span: Span,
msg: DiagMessage,
},
WithName {
span: Span,
msg: DiagMessage,
name: &'a str,
continues: bool,
},
WithRequirement {
span: Span,
requirement: ObligationCauseAsDiagArg<'a>,
expected_found: Option<(DiagStyledString, DiagStyledString)>,
},
}
impl Subdiagnostic for RegionOriginNote<'_> {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let mut label_or_note = |span, msg: DiagMessage| {
let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span);
if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
diag.span_label(span, msg);
} else if span_is_primary && expanded_sub_count == 0 {
diag.note(msg);
} else {
diag.span_note(span, msg);
}
};
match self {
RegionOriginNote::Plain { span, msg } => {
label_or_note(span, msg);
}
RegionOriginNote::WithName { span, msg, name, continues } => {
label_or_note(span, msg);
diag.arg("name", name);
diag.arg("continues", continues);
}
RegionOriginNote::WithRequirement {
span,
requirement,
expected_found: Some((expected, found)),
} => {
label_or_note(
span,
msg!(
"...so that the {$requirement ->
[method_compat] method type is compatible with trait
[type_compat] associated type is compatible with trait
[const_compat] const is compatible with trait
[expr_assignable] expression is assignable
[if_else_different] `if` and `else` have incompatible types
[no_else] `if` missing an `else` returns `()`
[fn_main_correct_type] `main` function has the correct type
[fn_lang_correct_type] lang item function has the correct type
[intrinsic_correct_type] intrinsic has the correct type
[method_correct_type] method receiver has the correct type
*[other] types are compatible
}"
),
);
diag.arg("requirement", requirement);
diag.note_expected_found("", expected, "", found);
}
RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => {
// FIXME: this really should be handled at some earlier stage. Our
// handling of region checking when type errors are present is
// *terrible*.
label_or_note(
span,
msg!(
"...so that {$requirement ->
[method_compat] method type is compatible with trait
[type_compat] associated type is compatible with trait
[const_compat] const is compatible with trait
[expr_assignable] expression is assignable
[if_else_different] `if` and `else` have incompatible types
[no_else] `if` missing an `else` returns `()`
[fn_main_correct_type] `main` function has the correct type
[fn_lang_correct_type] lang item function has the correct type
[intrinsic_correct_type] intrinsic has the correct type
[method_correct_type] method receiver has the correct type
*[other] types are compatible
}"
),
);
diag.arg("requirement", requirement);
}
};
}
}
pub enum LifetimeMismatchLabels {
InRet {
param_span: Span,
ret_span: Span,
span: Span,
label_var1: Option<Ident>,
},
Normal {
hir_equal: bool,
ty_sup: Span,
ty_sub: Span,
span: Span,
sup: Option<Ident>,
sub: Option<Ident>,
},
}
impl Subdiagnostic for LifetimeMismatchLabels {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self {
LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
diag.span_label(param_span, msg!("this parameter and the return type are declared with different lifetimes..."));
diag.span_label(ret_span, msg!("{\"\"}"));
diag.span_label(
span,
msg!(
"...but data{$label_var1_exists ->
[true] {\" \"}from `{$label_var1}`
*[false] {\"\"}
} is returned here"
),
);
diag.arg("label_var1_exists", label_var1.is_some());
diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
}
LifetimeMismatchLabels::Normal {
hir_equal,
ty_sup,
ty_sub,
span,
sup: label_var1,
sub: label_var2,
} => {
if hir_equal {
diag.span_label(
ty_sup,
msg!("this type is declared with multiple lifetimes..."),
);
diag.span_label(ty_sub, msg!("{\"\"}"));
diag.span_label(
span,
msg!("...but data with one lifetime flows into the other here"),
);
} else {
diag.span_label(
ty_sup,
msg!("these two types are declared with different lifetimes..."),
);
diag.span_label(ty_sub, msg!("{\"\"}"));
diag.span_label(
span,
msg!(
"...but data{$label_var1_exists ->
[true] {\" \"}from `{$label_var1}`
*[false] {\"\"}
} flows{$label_var2_exists ->
[true] {\" \"}into `{$label_var2}`
*[false] {\"\"}
} here"
),
);
diag.arg("label_var1_exists", label_var1.is_some());
diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
diag.arg("label_var2_exists", label_var2.is_some());
diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default());
}
}
}
}
}
pub struct AddLifetimeParamsSuggestion<'a> {
pub tcx: TyCtxt<'a>,
pub generic_param_scope: LocalDefId,
pub sub: Region<'a>,
pub ty_sup: &'a hir::Ty<'a>,
pub ty_sub: &'a hir::Ty<'a>,
pub add_note: bool,
}
impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let mut mk_suggestion = || {
let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub)
else {
return false;
};
let node = self.tcx.hir_node_by_def_id(anon_reg.scope);
let is_impl = matches!(&node, hir::Node::ImplItem(_));
let (generics, parent_generics) = match node {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. })
| hir::Node::TraitItem(hir::TraitItem { generics, .. })
| hir::Node::ImplItem(hir::ImplItem { generics, .. }) => (
generics,
match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.scope))
{
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Trait(_, _, _, _, generics, ..),
..
})
| hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { generics, .. }),
..
}) => Some(generics),
_ => None,
},
),
_ => return false,
};
let suggestion_param_name = generics
.params
.iter()
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
.map(|p| p.name.ident().name)
.find(|i| *i != kw::UnderscoreLifetime);
let introduce_new = suggestion_param_name.is_none();
let mut default = "'a".to_string();
if let Some(parent_generics) = parent_generics {
let used: FxHashSet<_> = parent_generics
.params
.iter()
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
.map(|p| p.name.ident().name)
.filter(|i| *i != kw::UnderscoreLifetime)
.map(|l| l.to_string())
.collect();
if let Some(lt) =
('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it))
{
// We want a lifetime that *isn't* present in the `trait` or `impl` that assoc
// `fn` belongs to. We could suggest reusing one of their lifetimes, but it is
// likely to be an over-constraining lifetime requirement, so we always add a
// lifetime to the `fn`.
default = lt;
}
}
let suggestion_param_name =
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default);
struct ImplicitLifetimeFinder {
suggestions: Vec<(Span, String)>,
suggestion_param_name: String,
}
impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
match ty.kind {
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
for segment in path.segments {
if let Some(args) = segment.args {
if args.args.iter().all(|arg| {
matches!(
arg,
hir::GenericArg::Lifetime(lifetime)
if lifetime.is_implicit()
)
}) {
self.suggestions.push((
segment.ident.span.shrink_to_hi(),
format!(
"<{}>",
args.args
.iter()
.map(|_| self.suggestion_param_name.clone())
.collect::<Vec<_>>()
.join(", ")
),
));
} else {
for arg in args.args {
if let hir::GenericArg::Lifetime(lifetime) = arg
&& lifetime.is_anonymous()
{
self.suggestions.push(
lifetime
.suggestion(&self.suggestion_param_name),
);
}
}
}
}
}
}
hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name));
}
_ => {}
}
walk_ty(self, ty);
}
}
let mut visitor = ImplicitLifetimeFinder {
suggestions: vec![],
suggestion_param_name: suggestion_param_name.clone(),
};
if let Some(fn_decl) = node.fn_decl()
&& let hir::FnRetTy::Return(ty) = fn_decl.output
{
visitor.visit_ty_unambig(ty);
}
if visitor.suggestions.is_empty() {
// Do not suggest constraining the `&self` param, but rather the return type.
// If that is wrong (because it is not sufficient), a follow up error will tell the
// user to fix it. This way we lower the chances of *over* constraining, but still
// get the cake of "correctly" constrained in two steps.
visitor.visit_ty_unambig(self.ty_sup);
}
visitor.visit_ty_unambig(self.ty_sub);
if visitor.suggestions.is_empty() {
return false;
}
if introduce_new {
let new_param_suggestion = if let Some(first) =
generics.params.iter().find(|p| !p.name.ident().span.is_empty())
{
(first.span.shrink_to_lo(), format!("{suggestion_param_name}, "))
} else {
(generics.span, format!("<{suggestion_param_name}>"))
};
visitor.suggestions.push(new_param_suggestion);
}
diag.multipart_suggestion(
msg!(
"consider {$is_reuse ->
[true] reusing
*[false] introducing
} a named lifetime parameter{$is_impl ->
[true] {\" \"}and update trait if needed
*[false] {\"\"}
}"
),
visitor.suggestions,
Applicability::MaybeIncorrect,
);
diag.arg("is_impl", is_impl);
diag.arg("is_reuse", !introduce_new);
true
};
if mk_suggestion() && self.add_note {
diag.note(msg!("each elided lifetime in input position becomes a distinct lifetime"));
}
}
}
#[derive(Diagnostic)]
#[diag("lifetime mismatch", code = E0623)]
pub struct LifetimeMismatch<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub labels: LifetimeMismatchLabels,
#[subdiagnostic]
pub suggestion: AddLifetimeParamsSuggestion<'a>,
}
pub struct IntroducesStaticBecauseUnmetLifetimeReq {
pub unmet_requirements: MultiSpan,
pub binding_span: Span,
}
impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
fn add_to_diag<G: EmissionGuarantee>(mut self, diag: &mut Diag<'_, G>) {
self.unmet_requirements.push_span_label(
self.binding_span,
msg!("introduces a `'static` lifetime requirement"),
);
diag.span_note(
self.unmet_requirements,
msg!("because this has an unmet lifetime requirement"),
);
}
}
// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that
#[derive(Subdiagnostic)]
pub enum DoesNotOutliveStaticFromImpl {
#[note(
"...does not necessarily outlive the static lifetime introduced by the compatible `impl`"
)]
Spanned {
#[primary_span]
span: Span,
},
#[note(
"...does not necessarily outlive the static lifetime introduced by the compatible `impl`"
)]
Unspanned,
}
#[derive(Subdiagnostic)]
pub enum ImplicitStaticLifetimeSubdiag {
#[note("this has an implicit `'static` lifetime requirement")]
Note {
#[primary_span]
span: Span,
},
#[suggestion(
"consider relaxing the implicit `'static` requirement",
style = "verbose",
code = " + '_",
applicability = "maybe-incorrect"
)]
Sugg {
#[primary_span]
span: Span,
},
}
#[derive(Diagnostic)]
#[diag("incompatible lifetime on type")]
pub struct MismatchedStaticLifetime<'a> {
#[primary_span]
pub cause_span: Span,
#[subdiagnostic]
pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq,
#[subdiagnostic]
pub expl: Option<note_and_explain::RegionExplanation<'a>>,
#[subdiagnostic]
pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl,
#[subdiagnostic]
pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>,
}
#[derive(Diagnostic)]
pub enum ExplicitLifetimeRequired<'a> {
#[diag("explicit lifetime required in the type of `{$simple_ident}`", code = E0621)]
WithIdent {
#[primary_span]
#[label("lifetime `{$named}` required")]
span: Span,
simple_ident: Ident,
named: String,
#[suggestion(
"add explicit lifetime `{$named}` to the type of `{$simple_ident}`",
code = "{new_ty}",
applicability = "unspecified",
style = "verbose"
)]
new_ty_span: Span,
#[skip_arg]
new_ty: Ty<'a>,
},
#[diag("explicit lifetime required in parameter type", code = E0621)]
WithParamType {
#[primary_span]
#[label("lifetime `{$named}` required")]
span: Span,
named: String,
#[suggestion(
"add explicit lifetime `{$named}` to type",
code = "{new_ty}",
applicability = "unspecified",
style = "verbose"
)]
new_ty_span: Span,
#[skip_arg]
new_ty: Ty<'a>,
},
}
pub enum TyOrSig<'tcx> {
Ty(Highlighted<'tcx, Ty<'tcx>>),
ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>),
}
impl IntoDiagArg for TyOrSig<'_> {
fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
match self {
TyOrSig::Ty(ty) => ty.into_diag_arg(path),
TyOrSig::ClosureSig(sig) => sig.into_diag_arg(path),
}
}
}
#[derive(Subdiagnostic)]
pub enum ActualImplExplNotes<'tcx> {
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedSignatureTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
lifetime_2: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`...")]
ExpectedSignatureAny {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...")]
ExpectedSignatureSome {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`"
)]
ExpectedSignatureNothing {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedPassiveTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
lifetime_2: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`...")]
ExpectedPassiveAny {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`...")]
ExpectedPassiveSome {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`"
)]
ExpectedPassiveNothing {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
},
#[note("{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")]
ExpectedOtherTwo {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
lifetime_2: usize,
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`..."
)]
ExpectedOtherAny {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`..."
)]
ExpectedOtherSome {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
},
#[note(
"{$leading_ellipsis ->
[true] ...
*[false] {\"\"}
}`{$ty_or_sig}` must implement `{$trait_path}`"
)]
ExpectedOtherNothing {
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
},
#[note(
"...but it actually implements `{$trait_path}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyImplementsTrait {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
has_lifetime: bool,
lifetime: usize,
},
#[note(
"...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyImplementedForTy {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
has_lifetime: bool,
lifetime: usize,
ty: String,
},
#[note(
"...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime ->
[true] , for some specific lifetime `'{$lifetime}`
*[false] {\"\"}
}"
)]
ButActuallyTyImplements {
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
has_lifetime: bool,
lifetime: usize,
ty: String,
},
}
pub enum ActualImplExpectedKind {
Signature,
Passive,
Other,
}
pub enum ActualImplExpectedLifetimeKind {
Two,
Any,
Some,
Nothing,
}
impl<'tcx> ActualImplExplNotes<'tcx> {
pub fn new_expected(
kind: ActualImplExpectedKind,
lt_kind: ActualImplExpectedLifetimeKind,
leading_ellipsis: bool,
ty_or_sig: TyOrSig<'tcx>,
trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
lifetime_1: usize,
lifetime_2: usize,
) -> Self {
match (kind, lt_kind) {
(ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => {
Self::ExpectedSignatureTwo {
leading_ellipsis,
ty_or_sig,
trait_path,
lifetime_1,
lifetime_2,
}
}
(ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => {
Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => {
Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => {
Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path }
}
(ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => {
Self::ExpectedPassiveTwo {
leading_ellipsis,
ty_or_sig,
trait_path,
lifetime_1,
lifetime_2,
}
}
(ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => {
Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => {
Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => {
Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path }
}
(ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => {
Self::ExpectedOtherTwo {
leading_ellipsis,
ty_or_sig,
trait_path,
lifetime_1,
lifetime_2,
}
}
(ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => {
Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => {
Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
}
(ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => {
Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path }
}
}
}
}
#[derive(Diagnostic)]
#[diag("implementation of `{$trait_def_id}` is not general enough")]
pub struct TraitPlaceholderMismatch<'tcx> {
#[primary_span]
pub span: Span,
#[label("doesn't satisfy where-clause")]
pub satisfy_span: Option<Span>,
#[label("due to a where-clause on `{$def_id}`...")]
pub where_span: Option<Span>,
#[label("implementation of `{$trait_def_id}` is not general enough")]
pub dup_span: Option<Span>,
pub def_id: String,
pub trait_def_id: String,
#[subdiagnostic]
pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>,
}
pub struct ConsiderBorrowingParamHelp {
pub spans: Vec<Span>,
}
impl Subdiagnostic for ConsiderBorrowingParamHelp {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let mut type_param_span: MultiSpan = self.spans.clone().into();
for &span in &self.spans {
// Seems like we can't call f() here as Into<DiagMessage> is required
type_param_span
.push_span_label(span, msg!("consider borrowing this type parameter in the trait"));
}
let msg = diag.eagerly_translate(msg!("the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`"));
diag.span_help(type_param_span, msg);
}
}
#[derive(Diagnostic)]
#[diag("`impl` item signature doesn't match `trait` item signature")]
pub struct TraitImplDiff {
#[primary_span]
#[label("found `{$found}`")]
pub sp: Span,
#[label("expected `{$expected}`")]
pub trait_sp: Span,
#[note(
"expected signature `{$expected}`
{\" \"}found signature `{$found}`"
)]
pub note: (),
#[subdiagnostic]
pub param_help: ConsiderBorrowingParamHelp,
#[help(
"verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output"
)]
pub rel_help: bool,
pub expected: String,
pub found: String,
}
pub struct DynTraitConstraintSuggestion {
pub span: Span,
pub ident: Ident,
}
impl Subdiagnostic for DynTraitConstraintSuggestion {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let mut multi_span: MultiSpan = vec![self.span].into();
multi_span.push_span_label(
self.span,
msg!("this has an implicit `'static` lifetime requirement"),
);
multi_span.push_span_label(
self.ident.span,
msg!("calling this method introduces the `impl`'s `'static` requirement"),
);
let msg = diag.eagerly_translate(msg!("the used `impl` has a `'static` requirement"));
diag.span_note(multi_span, msg);
let msg =
diag.eagerly_translate(msg!("consider relaxing the implicit `'static` requirement"));
diag.span_suggestion_verbose(
self.span.shrink_to_hi(),
msg,
" + '_",
Applicability::MaybeIncorrect,
);
}
}
#[derive(Diagnostic)]
#[diag("{$has_param_name ->
[true] `{$param_name}`
*[false] `fn` parameter
} has {$lifetime_kind ->
[true] lifetime `{$lifetime}`
*[false] an anonymous lifetime `'_`
} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement", code = E0772)]
pub struct ButCallingIntroduces {
#[label(
"{$has_lifetime ->
[true] lifetime `{$lifetime}`
*[false] an anonymous lifetime `'_`
}"
)]
pub param_ty_span: Span,
#[primary_span]
#[label("...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path ->
[true] `impl` of `{$impl_path}`
*[false] inherent `impl`
}")]
pub cause_span: Span,
pub has_param_name: bool,
pub param_name: String,
pub has_lifetime: bool,
pub lifetime: String,
pub assoc_item: Symbol,
pub has_impl_path: bool,
pub impl_path: String,
}
pub struct ReqIntroducedLocations {
pub span: MultiSpan,
pub spans: Vec<Span>,
pub fn_decl_span: Span,
pub cause_span: Span,
pub add_label: bool,
}
impl Subdiagnostic for ReqIntroducedLocations {
fn add_to_diag<G: EmissionGuarantee>(mut self, diag: &mut Diag<'_, G>) {
for sp in self.spans {
self.span.push_span_label(sp, msg!("`'static` requirement introduced here"));
}
if self.add_label {
self.span.push_span_label(
self.fn_decl_span,
msg!("requirement introduced by this return type"),
);
}
self.span.push_span_label(self.cause_span, msg!("because of this returned expression"));
let msg = diag.eagerly_translate(msg!(
"\"`'static` lifetime requirement introduced by the return type"
));
diag.span_note(self.span, msg);
}
}
#[derive(Diagnostic)]
#[diag("{$has_param_name ->
[true] `{$param_name}`
*[false] `fn` parameter
} has {$has_lifetime ->
[true] lifetime `{$lifetime}`
*[false] an anonymous lifetime `'_`
} but it needs to satisfy a `'static` lifetime requirement", code = E0759)]
pub struct ButNeedsToSatisfy {
#[primary_span]
pub sp: Span,
#[label(
"this data with {$has_lifetime ->
[true] lifetime `{$lifetime}`
*[false] an anonymous lifetime `'_`
}..."
)]
pub influencer_point: Span,
#[label("...is used here...")]
pub spans: Vec<Span>,
#[label(
"{$spans_empty ->
*[true] ...is used and required to live as long as `'static` here
[false] ...and is required to live as long as `'static` here
}"
)]
pub require_span_as_label: Option<Span>,
#[note(
"{$spans_empty ->
*[true] ...is used and required to live as long as `'static` here
[false] ...and is required to live as long as `'static` here
}"
)]
pub require_span_as_note: Option<Span>,
#[note("`'static` lifetime requirement introduced by this bound")]
pub bound: Option<Span>,
pub has_param_name: bool,
pub param_name: String,
pub spans_empty: bool,
pub has_lifetime: bool,
pub lifetime: String,
}
#[derive(Diagnostic)]
#[diag("lifetime of reference outlives lifetime of borrowed content...", code = E0312)]
pub struct OutlivesContent<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag("lifetime of the source pointer does not outlive lifetime bound of the object type", code = E0476)]
pub struct OutlivesBound<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag("the type `{$ty}` does not fulfill the required lifetime", code = E0477)]
pub struct FulfillReqLifetime<'a> {
#[primary_span]
pub span: Span,
pub ty: Ty<'a>,
#[subdiagnostic]
pub note: Option<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag("lifetime bound not satisfied", code = E0478)]
pub struct LfBoundNotSatisfied<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag("in type `{$ty}`, reference has a longer lifetime than the data it references", code = E0491)]
pub struct RefLongerThanData<'a> {
#[primary_span]
pub span: Span,
pub ty: Ty<'a>,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Subdiagnostic)]
pub enum WhereClauseSuggestions {
#[suggestion(
"remove the `where` clause",
code = "",
applicability = "machine-applicable",
style = "verbose"
)]
Remove {
#[primary_span]
span: Span,
},
#[suggestion(
"copy the `where` clause predicates from the trait",
code = "{space}where {trait_predicates}",
applicability = "machine-applicable",
style = "verbose"
)]
CopyPredicates {
#[primary_span]
span: Span,
space: &'static str,
trait_predicates: String,
},
}
#[derive(Subdiagnostic)]
pub enum SuggestRemoveSemiOrReturnBinding {
#[multipart_suggestion(
"consider removing this semicolon and boxing the expressions",
applicability = "machine-applicable"
)]
RemoveAndBox {
#[suggestion_part(code = "Box::new(")]
first_lo: Span,
#[suggestion_part(code = ")")]
first_hi: Span,
#[suggestion_part(code = "Box::new(")]
second_lo: Span,
#[suggestion_part(code = ")")]
second_hi: Span,
#[suggestion_part(code = "")]
sp: Span,
},
#[suggestion(
"consider removing this semicolon",
style = "short",
code = "",
applicability = "machine-applicable"
)]
Remove {
#[primary_span]
sp: Span,
},
#[suggestion(
"consider returning the local binding `{$ident}`",
style = "verbose",
code = "{code}",
applicability = "maybe-incorrect"
)]
Add {
#[primary_span]
sp: Span,
code: String,
ident: Ident,
},
#[note("consider returning one of these bindings")]
AddOne {
#[primary_span]
spans: MultiSpan,
},
}
#[derive(Subdiagnostic)]
pub enum ConsiderAddingAwait {
#[help("consider `await`ing on both `Future`s")]
BothFuturesHelp,
#[multipart_suggestion(
"consider `await`ing on both `Future`s",
applicability = "maybe-incorrect"
)]
BothFuturesSugg {
#[suggestion_part(code = ".await")]
first: Span,
#[suggestion_part(code = ".await")]
second: Span,
},
#[suggestion(
"consider `await`ing on the `Future`",
code = ".await",
style = "verbose",
applicability = "maybe-incorrect"
)]
FutureSugg {
#[primary_span]
span: Span,
},
#[note("calling an async function returns a future")]
FutureSuggNote {
#[primary_span]
span: Span,
},
#[multipart_suggestion(
"consider `await`ing on the `Future`",
style = "verbose",
applicability = "maybe-incorrect"
)]
FutureSuggMultiple {
#[suggestion_part(code = ".await")]
spans: Vec<Span>,
},
}
#[derive(Diagnostic)]
pub enum PlaceholderRelationLfNotSatisfied {
#[diag("lifetime bound not satisfied")]
HasBoth {
#[primary_span]
span: Span,
#[note("the lifetime `{$sub_symbol}` defined here...")]
sub_span: Span,
#[note("...must outlive the lifetime `{$sup_symbol}` defined here")]
sup_span: Span,
sub_symbol: Symbol,
sup_symbol: Symbol,
#[note(
"this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"
)]
note: (),
},
#[diag("lifetime bound not satisfied")]
HasSub {
#[primary_span]
span: Span,
#[note("the lifetime `{$sub_symbol}` defined here...")]
sub_span: Span,
#[note("...must outlive the lifetime defined here")]
sup_span: Span,
sub_symbol: Symbol,
#[note(
"this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"
)]
note: (),
},
#[diag("lifetime bound not satisfied")]
HasSup {
#[primary_span]
span: Span,
#[note("the lifetime defined here...")]
sub_span: Span,
#[note("...must outlive the lifetime `{$sup_symbol}` defined here")]
sup_span: Span,
sup_symbol: Symbol,
#[note(
"this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"
)]
note: (),
},
#[diag("lifetime bound not satisfied")]
HasNone {
#[primary_span]
span: Span,
#[note("the lifetime defined here...")]
sub_span: Span,
#[note("...must outlive the lifetime defined here")]
sup_span: Span,
#[note(
"this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"
)]
note: (),
},
#[diag("lifetime bound not satisfied")]
OnlyPrimarySpan {
#[primary_span]
span: Span,
#[note(
"this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"
)]
note: (),
},
}
#[derive(Diagnostic)]
#[diag("hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds", code = E0700)]
pub struct OpaqueCapturesLifetime<'tcx> {
#[primary_span]
pub span: Span,
#[label("opaque type defined here")]
pub opaque_ty_span: Span,
pub opaque_ty: Ty<'tcx>,
}
#[derive(Subdiagnostic)]
pub enum FunctionPointerSuggestion<'a> {
#[suggestion(
"consider using a reference",
code = "&",
style = "verbose",
applicability = "maybe-incorrect"
)]
UseRef {
#[primary_span]
span: Span,
},
#[suggestion(
"consider removing the reference",
code = "{fn_name}",
style = "verbose",
applicability = "maybe-incorrect"
)]
RemoveRef {
#[primary_span]
span: Span,
#[skip_arg]
fn_name: String,
},
#[suggestion(
"consider casting to a fn pointer",
code = "&({fn_name} as {sig})",
style = "verbose",
applicability = "maybe-incorrect"
)]
CastRef {
#[primary_span]
span: Span,
#[skip_arg]
fn_name: String,
#[skip_arg]
sig: Binder<'a, FnSig<'a>>,
},
#[suggestion(
"consider casting to a fn pointer",
code = " as {sig}",
style = "verbose",
applicability = "maybe-incorrect"
)]
Cast {
#[primary_span]
span: Span,
#[skip_arg]
sig: Binder<'a, FnSig<'a>>,
},
#[suggestion(
"consider casting both fn items to fn pointers using `as {$expected_sig}`",
code = " as {found_sig}",
style = "hidden",
applicability = "maybe-incorrect"
)]
CastBoth {
#[primary_span]
span: Span,
#[skip_arg]
found_sig: Binder<'a, FnSig<'a>>,
expected_sig: Binder<'a, FnSig<'a>>,
},
#[suggestion(
"consider casting both fn items to fn pointers using `as {$expected_sig}`",
code = "&({fn_name} as {found_sig})",
style = "hidden",
applicability = "maybe-incorrect"
)]
CastBothRef {
#[primary_span]
span: Span,
#[skip_arg]
fn_name: String,
#[skip_arg]
found_sig: Binder<'a, FnSig<'a>>,
expected_sig: Binder<'a, FnSig<'a>>,
},
}
#[derive(Subdiagnostic)]
#[note("fn items are distinct from fn pointers")]
pub struct FnItemsAreDistinct;
#[derive(Subdiagnostic)]
#[note("different fn items have unique types, even if their signatures are the same")]
pub struct FnUniqTypes;
#[derive(Subdiagnostic)]
#[help("consider casting the fn item to a fn pointer: `{$casting}`")]
pub struct FnConsiderCasting {
pub casting: String,
}
#[derive(Subdiagnostic)]
#[help("consider casting both fn items to fn pointers using `as {$sig}`")]
pub struct FnConsiderCastingBoth<'a> {
pub sig: Binder<'a, FnSig<'a>>,
}
#[derive(Subdiagnostic)]
pub enum SuggestAccessingField<'a> {
#[suggestion(
"you might have meant to use field `{$name}` whose type is `{$ty}`",
code = "{snippet}.{name}",
applicability = "maybe-incorrect",
style = "verbose"
)]
Safe {
#[primary_span]
span: Span,
snippet: String,
name: Symbol,
ty: Ty<'a>,
},
#[suggestion(
"you might have meant to use field `{$name}` whose type is `{$ty}`",
code = "unsafe {{ {snippet}.{name} }}",
applicability = "maybe-incorrect",
style = "verbose"
)]
Unsafe {
#[primary_span]
span: Span,
snippet: String,
name: Symbol,
ty: Ty<'a>,
},
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"try wrapping the pattern in `{$variant}`",
applicability = "maybe-incorrect"
)]
pub struct SuggestTuplePatternOne {
pub variant: String,
#[suggestion_part(code = "{variant}(")]
pub span_low: Span,
#[suggestion_part(code = ")")]
pub span_high: Span,
}
pub struct SuggestTuplePatternMany {
pub path: String,
pub cause_span: Span,
pub compatible_variants: Vec<String>,
}
impl Subdiagnostic for SuggestTuplePatternMany {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
diag.arg("path", self.path);
let message =
diag.eagerly_translate(msg!("try wrapping the pattern in a variant of `{$path}`"));
diag.multipart_suggestions(
message,
self.compatible_variants.into_iter().map(|variant| {
vec![
(self.cause_span.shrink_to_lo(), format!("{variant}(")),
(self.cause_span.shrink_to_hi(), ")".to_string()),
]
}),
rustc_errors::Applicability::MaybeIncorrect,
);
}
}
#[derive(Subdiagnostic)]
pub enum TypeErrorAdditionalDiags {
#[suggestion(
"if you meant to write a byte literal, prefix with `b`",
code = "b'{code}'",
applicability = "machine-applicable"
)]
MeantByteLiteral {
#[primary_span]
span: Span,
code: String,
},
#[suggestion(
"if you meant to write a `char` literal, use single quotes",
code = "'{code}'",
applicability = "machine-applicable"
)]
MeantCharLiteral {
#[primary_span]
span: Span,
code: String,
},
#[multipart_suggestion(
"if you meant to write a string literal, use double quotes",
applicability = "machine-applicable"
)]
MeantStrLiteral {
#[suggestion_part(code = "\"")]
start: Span,
#[suggestion_part(code = "\"")]
end: Span,
},
#[suggestion(
"consider specifying the actual array length",
code = "{length}",
applicability = "maybe-incorrect"
)]
ConsiderSpecifyingLength {
#[primary_span]
span: Span,
length: u64,
},
#[note("`?` operator cannot convert from `{$found}` to `{$expected}`")]
TryCannotConvert { found: String, expected: String },
#[suggestion(
"use a trailing comma to create a tuple with one element",
code = ",",
applicability = "machine-applicable"
)]
TupleOnlyComma {
#[primary_span]
span: Span,
},
#[multipart_suggestion(
"use a trailing comma to create a tuple with one element",
applicability = "machine-applicable"
)]
TupleAlsoParentheses {
#[suggestion_part(code = "(")]
span_low: Span,
#[suggestion_part(code = ",)")]
span_high: Span,
},
#[suggestion(
"consider adding `let`",
style = "verbose",
applicability = "machine-applicable",
code = "let "
)]
AddLetForLetChains {
#[primary_span]
span: Span,
},
}
#[derive(Diagnostic)]
pub enum ObligationCauseFailureCode {
#[diag("method not compatible with trait", code = E0308)]
MethodCompat {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("type not compatible with trait", code = E0308)]
TypeCompat {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("const not compatible with trait", code = E0308)]
ConstCompat {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("`?` operator has incompatible types", code = E0308)]
TryCompat {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("`match` arms have incompatible types", code = E0308)]
MatchCompat {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("`if` and `else` have incompatible types", code = E0308)]
IfElseDifferent {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("`if` may be missing an `else` clause", code = E0317)]
NoElse {
#[primary_span]
span: Span,
},
#[diag("`else` clause of `let...else` does not diverge", code = E0308)]
NoDiverge {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("`main` function has wrong type", code = E0580)]
FnMainCorrectType {
#[primary_span]
span: Span,
},
#[diag(
"{$lang_item_name ->
[panic_impl] `#[panic_handler]`
*[lang_item_name] lang item `{$lang_item_name}`
} function has wrong type"
, code = E0308)]
FnLangCorrectType {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
lang_item_name: Symbol,
},
#[diag("intrinsic has wrong type", code = E0308)]
IntrinsicCorrectType {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("mismatched `self` parameter type", code = E0308)]
MethodCorrectType {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("closure/coroutine type that references itself", code = E0644)]
ClosureSelfref {
#[primary_span]
span: Span,
},
#[diag("cannot coerce functions which must be inlined to function pointers", code = E0308)]
CantCoerceForceInline {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("cannot coerce intrinsics to function pointers", code = E0308)]
CantCoerceIntrinsic {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
#[diag("mismatched types", code = E0308)]
Generic {
#[primary_span]
span: Span,
#[subdiagnostic]
subdiags: Vec<TypeErrorAdditionalDiags>,
},
}
#[derive(Subdiagnostic)]
pub enum AddPreciseCapturing {
#[suggestion(
"add a `use<...>` bound to explicitly capture `{$new_lifetime}`",
style = "verbose",
code = " + use<{concatenated_bounds}>",
applicability = "machine-applicable"
)]
New {
#[primary_span]
span: Span,
new_lifetime: Symbol,
concatenated_bounds: String,
},
#[suggestion(
"add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it",
style = "verbose",
code = "{pre}{new_lifetime}{post}",
applicability = "machine-applicable"
)]
Existing {
#[primary_span]
span: Span,
new_lifetime: Symbol,
pre: &'static str,
post: &'static str,
},
}
pub struct AddPreciseCapturingAndParams {
pub suggs: Vec<(Span, String)>,
pub new_lifetime: Symbol,
pub apit_spans: Vec<Span>,
}
impl Subdiagnostic for AddPreciseCapturingAndParams {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
diag.arg("new_lifetime", self.new_lifetime);
diag.multipart_suggestion(
msg!("add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate"),
self.suggs,
Applicability::MaybeIncorrect,
);
diag.span_note(
self.apit_spans,
msg!("you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable"),
);
}
}
/// Given a set of captured `DefId` for an RPIT (opaque_def_id) and a given
/// function (fn_def_id), try to suggest adding `+ use<...>` to capture just
/// the specified parameters. If one of those parameters is an APIT, then try
/// to suggest turning it into a regular type parameter.
pub fn impl_trait_overcapture_suggestion<'tcx>(
tcx: TyCtxt<'tcx>,
opaque_def_id: LocalDefId,
fn_def_id: LocalDefId,
captured_args: FxIndexSet<DefId>,
) -> Option<AddPreciseCapturingForOvercapture> {
let generics = tcx.generics_of(fn_def_id);
let mut captured_lifetimes = FxIndexSet::default();
let mut captured_non_lifetimes = FxIndexSet::default();
let mut synthetics = vec![];
for arg in captured_args {
if tcx.def_kind(arg) == DefKind::LifetimeParam {
captured_lifetimes.insert(tcx.item_name(arg));
} else {
let idx = generics.param_def_id_to_index(tcx, arg).expect("expected arg in scope");
let param = generics.param_at(idx as usize, tcx);
if param.kind.is_synthetic() {
synthetics.push((tcx.def_span(arg), param.name));
} else {
captured_non_lifetimes.insert(tcx.item_name(arg));
}
}
}
let mut next_fresh_param = || {
["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
.into_iter()
.map(Symbol::intern)
.chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
.find(|s| captured_non_lifetimes.insert(*s))
.unwrap()
};
let mut suggs = vec![];
let mut apit_spans = vec![];
if !synthetics.is_empty() {
let mut new_params = String::new();
for (i, (span, name)) in synthetics.into_iter().enumerate() {
apit_spans.push(span);
let fresh_param = next_fresh_param();
// Suggest renaming.
suggs.push((span, fresh_param.to_string()));
// Super jank. Turn `impl Trait` into `T: Trait`.
//
// This currently involves stripping the `impl` from the name of
// the parameter, since APITs are always named after how they are
// rendered in the AST. This sucks! But to recreate the bound list
// from the APIT itself would be miserable, so we're stuck with
// this for now!
if i > 0 {
new_params += ", ";
}
let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
new_params += fresh_param.as_str();
new_params += ": ";
new_params += name_as_bounds;
}
let Some(generics) = tcx.hir_get_generics(fn_def_id) else {
// This shouldn't happen, but don't ICE.
return None;
};
// Add generics or concatenate to the end of the list.
suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
(params_span, format!(", {new_params}"))
} else {
(generics.span, format!("<{new_params}>"))
});
}
let concatenated_bounds = captured_lifetimes
.into_iter()
.chain(captured_non_lifetimes)
.map(|sym| sym.to_string())
.collect::<Vec<_>>()
.join(", ");
let opaque_hir_id = tcx.local_def_id_to_hir_id(opaque_def_id);
// FIXME: This is a bit too conservative, since it ignores parens already written in AST.
let (lparen, rparen) = match tcx
.hir_parent_iter(opaque_hir_id)
.nth(1)
.expect("expected ty to have a parent always")
.1
{
Node::PathSegment(segment)
if segment.args().paren_sugar_output().is_some_and(|ty| ty.hir_id == opaque_hir_id) =>
{
("(", ")")
}
Node::Ty(ty) => match ty.kind {
rustc_hir::TyKind::Ptr(_) | rustc_hir::TyKind::Ref(..) => ("(", ")"),
// FIXME: RPITs are not allowed to be nested in `impl Fn() -> ...`,
// but we eventually could support that, and that would necessitate
// making this more sophisticated.
_ => ("", ""),
},
_ => ("", ""),
};
let rpit_span = tcx.def_span(opaque_def_id);
if !lparen.is_empty() {
suggs.push((rpit_span.shrink_to_lo(), lparen.to_string()));
}
suggs.push((rpit_span.shrink_to_hi(), format!(" + use<{concatenated_bounds}>{rparen}")));
Some(AddPreciseCapturingForOvercapture { suggs, apit_spans })
}
pub struct AddPreciseCapturingForOvercapture {
pub suggs: Vec<(Span, String)>,
pub apit_spans: Vec<Span>,
}
impl Subdiagnostic for AddPreciseCapturingForOvercapture {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let applicability = if self.apit_spans.is_empty() {
Applicability::MachineApplicable
} else {
// If there are APIT that are converted to regular parameters,
// then this may make the API turbofishable in ways that were
// not intended.
Applicability::MaybeIncorrect
};
diag.multipart_suggestion(
msg!("use the precise capturing `use<...>` syntax to make the captures explicit"),
self.suggs,
applicability,
);
if !self.apit_spans.is_empty() {
diag.span_note(
self.apit_spans,
msg!("you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable"),
);
}
}
}
#[derive(Diagnostic)]
#[diag("expected generic {$kind} parameter, found `{$arg}`", code = E0792)]
pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
pub arg: GenericArg<'tcx>,
pub kind: &'a str,
#[primary_span]
pub span: Span,
#[label("{STREQ($arg, \"'static\") ->
[true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
*[other] this generic parameter must be used with a generic {$kind} parameter
}")]
pub param_span: Span,
}