diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index ea5e81c3db816..14a97f38bac22 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -23,6 +23,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem pub(crate) mod do_not_recommend; pub(crate) mod on_const; pub(crate) mod on_move; +pub(crate) mod on_type_error; pub(crate) mod on_unimplemented; pub(crate) mod on_unknown; @@ -38,6 +39,8 @@ pub(crate) enum Mode { DiagnosticOnMove, /// `#[diagnostic::on_unknown]` DiagnosticOnUnknown, + /// `#[diagnostic::on_type_error]` + DiagnosticOnTypeError, } impl Mode { @@ -48,12 +51,15 @@ impl Mode { Self::DiagnosticOnConst => "diagnostic::on_const", Self::DiagnosticOnMove => "diagnostic::on_move", Self::DiagnosticOnUnknown => "diagnostic::on_unknown", + Self::DiagnosticOnTypeError => "diagnostic::on_type_error", } } fn expected_options(&self) -> &'static str { const DEFAULT: &str = "at least one of the `message`, `note` and `label` options are expected"; + const DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS: &str = + "at least a single `note` option is expected"; match self { Self::RustcOnUnimplemented => { "see " @@ -62,11 +68,14 @@ impl Mode { Self::DiagnosticOnConst => DEFAULT, Self::DiagnosticOnMove => DEFAULT, Self::DiagnosticOnUnknown => DEFAULT, + Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS, } } fn allowed_options(&self) -> &'static str { const DEFAULT: &str = "only `message`, `note` and `label` are allowed as options"; + const DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS: &str = + "only `note` is allowed as option for `diagnostic::on_type_error`"; match self { Self::RustcOnUnimplemented => { "see " @@ -75,6 +84,7 @@ impl Mode { Self::DiagnosticOnConst => DEFAULT, Self::DiagnosticOnMove => DEFAULT, Self::DiagnosticOnUnknown => DEFAULT, + Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS, } } } @@ -268,6 +278,10 @@ fn parse_directive_items<'p, S: Stage>( } }; match (mode, name) { + (Mode::DiagnosticOnTypeError, sym::message) + | (Mode::DiagnosticOnTypeError, sym::label) => { + malformed!() + } (_, sym::message) => { let value = or_malformed!(value?); if let Some(message) = &message { @@ -332,7 +346,6 @@ fn parse_directive_items<'p, S: Stage>( malformed!(); } } - _other => { malformed!(); } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs new file mode 100644 index 0000000000000..6af57703addf1 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs @@ -0,0 +1,61 @@ +use rustc_feature::template; +use rustc_hir::attrs::AttributeKind; +use rustc_span::sym; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::{ALL_TARGETS, AllowedTargets}; + +#[derive(Default)] +pub(crate) struct OnTypeErrorParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnTypeErrorParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + if !cx.features().diagnostic_on_type_error() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs + return; + } + + let span = cx.attr_span; + self.span = Some(span); + + let Some(items) = parse_list(cx, args, mode) else { return }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + } + } +} + +impl AttributeParser for OnTypeErrorParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_type_error], + template!(List: &[r#" note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnTypeError); + }, + )]; + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnTypeError { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8d92ec50e10c4..3710daefcc0a8 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -31,6 +31,7 @@ use crate::attributes::deprecation::*; use crate::attributes::diagnostic::do_not_recommend::*; use crate::attributes::diagnostic::on_const::*; use crate::attributes::diagnostic::on_move::*; +use crate::attributes::diagnostic::on_type_error::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::diagnostic::on_unknown::*; use crate::attributes::doc::*; @@ -156,6 +157,7 @@ attribute_parsers!( NakedParser, OnConstParser, OnMoveParser, + OnTypeErrorParser, OnUnimplementedParser, OnUnknownParser, RustcAlignParser, diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index a927c30fae325..8518b33cc28d3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -509,6 +509,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( .try_report_from_nll() .or_else(|| { if let SubregionOrigin::Subtype(trace) = cause { + tracing::info!("borrow checker"); Some(infcx.err_ctxt().report_and_explain_type_error( *trace, infcx.tcx.param_env(generic_param_scope), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1785014954093..73eb95b4f22f7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -476,6 +476,8 @@ declare_features! ( (unstable, diagnostic_on_const, "1.93.0", Some(143874)), /// Allows giving on-move borrowck custom diagnostic messages for a type (unstable, diagnostic_on_move, "1.96.0", Some(154181)), + /// Allows giving custom types diagnostic messages on type errors + (unstable, diagnostic_on_type_error, "CURRENT_RUSTC_VERSION", Some(155382)), /// Allows giving unresolved imports a custom diagnostic message (unstable, diagnostic_on_unknown, "1.96.0", Some(152900)), /// Allows `#[doc(cfg(...))]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 05f398058ecaa..b781e72719cb4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1194,6 +1194,12 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents`#[diagnostic::on_type_error]`. + OnTypeError { + span: Span, + directive: Option>, + }, + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. OnUnimplemented { span: Span, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index ad4d0728888bf..fe7e64f0a16f7 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -78,6 +78,7 @@ impl AttributeKind { NonExhaustive(..) => Yes, // Needed for rustdoc OnConst { .. } => Yes, OnMove { .. } => Yes, + OnTypeError { .. } => Yes, OnUnimplemented { .. } => Yes, OnUnknown { .. } => Yes, Optimize(..) => No, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2d9be11cb3057..1a4a5645e9f6d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -81,6 +81,7 @@ declare_lint_pass! { NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, + ON_TYPE_ERROR_MULTIPLE_GENERICS, OUT_OF_SCOPE_MACRO_CALLS, OVERLAPPING_RANGE_ENDPOINTS, PATTERNS_IN_FNS_WITHOUT_BODY, @@ -4574,6 +4575,43 @@ declare_lint! { Warn, "detects diagnostic attribute with malformed diagnostic format literals", } + +declare_lint! { + /// The `on_type_error_multiple_generics` lint detects when + /// `#[diagnostic::on_type_error]` is used on items with more than one generic parameter. + /// + /// ### Example + /// + /// ```rust,ignore (requires nightly feature) + /// #![feature(diagnostic_on_type_error)] + /// #[diagnostic::on_type_error(note = "too many generics")] + /// struct TooMany(T, U); + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: `#[diagnostic::on_type_error]` only supports one ADT generic parameter, but found `2` + /// --> lint_example.rs:3:15 + /// | + /// 3 | struct TooMany(T, U); + /// | ^^^^^^ + /// | + /// = note: `#[warn(on_type_error_multiple_generics)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The `#[diagnostic::on_type_error]` attribute currently only supports items + /// with a single generic parameter. Using it on an item with multiple generic + /// parameters will cause the attribute to be ignored. + /// + /// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace + pub ON_TYPE_ERROR_MULTIPLE_GENERICS, + Warn, + "detects use of #[diagnostic::on_type_error] with multiple generic parameters", +} + declare_lint! { /// The `ambiguous_glob_imports` lint detects glob imports that should report ambiguity /// errors, but previously didn't do that due to rustc bugs. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 24e9b71de5914..eb8fc3ad94285 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -39,7 +39,7 @@ use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, ON_TYPE_ERROR_MULTIPLE_GENERICS, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -79,6 +79,10 @@ struct DiagnosticOnUnknownOnlyForImports { item_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_type_error]` can only be applied to enums, structs or unions")] +struct DiagnosticOnTypeErrorOnlyForAdt; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -228,6 +232,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::OnMove { span, directive }) => { self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref()) }, + Attribute::Parsed(AttributeKind::OnTypeError{ span, directive }) => { + self.check_diagnostic_on_type_error(*span, hir_id, target, directive.as_deref()) + }, Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -688,6 +695,74 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks if `#[diagnostic::on_type_error]` is applied to an ADT definition + fn check_diagnostic_on_type_error( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&Directive>, + ) { + if !matches!(target, Target::Enum | Target::Struct | Target::Union) { + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnTypeErrorOnlyForAdt, + ); + } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: + ItemKind::Struct(_, generics, _) + | ItemKind::Enum(_, generics, _) + | ItemKind::Union(_, generics, _), + .. + }) = self.tcx.hir_node(hir_id) + { + let generic_count = generics + .params + .iter() + .filter(|p| !matches!(p.kind, GenericParamKind::Lifetime { .. })) + .count(); + + // Enforce: at most one generic + if generic_count > 1 { + self.tcx.emit_node_span_lint( + ON_TYPE_ERROR_MULTIPLE_GENERICS, + hir_id, + generics.span, + errors::OnTypeErrorMultipleGenerics { count: generic_count }, + ); + } + + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + + let is_allowed = argument_name == sym::Expected || argument_name == sym::Found; + if !(has_generic | is_allowed) { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::OnTypeErrorMalformedFormatLiterals { name: argument_name }, + ) + } + }); + } + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index cb56a28b8628a..9c7e7f5a08de4 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1307,3 +1307,18 @@ pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { pub(crate) struct OnMoveMalformedFormatLiterals { pub name: Symbol, } + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"expect either a generic argument name, {"`{Self}`"}, {"`{Expected}`"} or {"`{Found}`"} as format argument"#)] +pub(crate) struct OnTypeErrorMalformedFormatLiterals { + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag( + "`#[diagnostic::on_type_error]` only supports one ADT generic parameter, but found `{$count}`" +)] +pub(crate) struct OnTypeErrorMultipleGenerics { + pub count: usize, +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 2de4e21b1e96a..02e00139b5bca 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -718,6 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (sym::on_move, Some(sym::diagnostic_on_move)), (sym::on_const, Some(sym::diagnostic_on_const)), (sym::on_unknown, Some(sym::diagnostic_on_unknown)), + (sym::on_type_error, Some(sym::diagnostic_on_type_error)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..352b4a41ca87b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -213,6 +213,7 @@ symbols! { Eq, Equal, Err, + Expected, // used for formatting for diagnostic::on_type_error for diagnostic purposes ExternC, ExternRust, Float, @@ -223,6 +224,7 @@ symbols! { FnPtr, Formatter, Forward, + Found, // used for formatting for diagnostic::on_type_error for diagnostic purposes From, FromIterator, FromResidual, @@ -802,6 +804,7 @@ symbols! { diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, + diagnostic_on_type_error, diagnostic_on_unknown, dialect, direct, @@ -1425,6 +1428,7 @@ symbols! { on, on_const, on_move, + on_type_error, on_unimplemented, on_unknown, opaque, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 4ac00cef6b9ce..9635ba7c40109 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -51,12 +51,13 @@ use std::path::PathBuf; use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; -use rustc_hir as hir; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_macros::extension; use rustc_middle::bug; @@ -67,7 +68,8 @@ use rustc_middle::ty::{ self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, kw, sym}; +use thin_vec::ThinVec; use tracing::{debug, instrument}; use crate::error_reporting::TypeErrCtxt; @@ -1978,6 +1980,83 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + fn check_on_type_error_attribute( + &self, + expected_ty: Ty<'tcx>, + found_ty: Ty<'tcx>, + ) -> ThinVec { + let mut unique_notes = FxHashSet::default(); + + // Check found type for attribute + if let ty::Adt(item_def, args) = found_ty.kind() { + if let Some(Some(directive)) = + find_attr!(self.tcx, item_def.did(), OnTypeError { directive, .. } => directive) + { + let notes = self.format_on_type_error_notes( + directive, + args, + item_def.clone(), + expected_ty, + found_ty, + ); + unique_notes.extend(notes); + } + } + + // Check expected type for attribute + if let ty::Adt(item_def, args) = expected_ty.kind() { + if let Some(Some(directive)) = + find_attr!(self.tcx, item_def.did(), OnTypeError { directive, .. } => directive) + { + let notes = self.format_on_type_error_notes( + directive, + args, + item_def.clone(), + expected_ty, + found_ty, + ); + unique_notes.extend(notes); + } + } + + // Order doesn't matter for notes, so we can allow the lint + #[allow(rustc::potential_query_instability)] + unique_notes.into_iter().collect() + } + + fn format_on_type_error_notes( + &self, + directive: &Directive, + args: &ty::GenericArgsRef<'tcx>, + item_def: ty::AdtDef<'tcx>, + expected_ty: Ty<'tcx>, + found_ty: Ty<'tcx>, + ) -> ThinVec { + let item_name = self.tcx.item_name(item_def.did()).to_string(); + let mut generic_args: Vec<_> = self + .tcx + .generics_of(item_def.did()) + .own_params + .iter() + .filter_map(|param| Some((param.name, args[param.index as usize].to_string()))) + .collect(); + generic_args.push((kw::SelfUpper, item_name.clone())); + generic_args.push((sym::Expected, expected_ty.to_string())); + generic_args.push((sym::Found, found_ty.to_string())); + + let format_args = FormatArgs { + this: item_name, + // Unused + this_sugared: String::new(), + // Unused + item_context: "", + generic_args, + }; + let CustomDiagnostic { notes, .. } = directive.eval(None, &format_args); + + notes.into() + } + pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, @@ -1988,6 +2067,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let span = trace.cause.span; let mut path = None; + + // Check for on_type_error attribute + let on_type_error_notes = if let Some((expected_ty, found_ty)) = trace.values.ty() { + self.check_on_type_error_attribute(expected_ty, found_ty) + } else { + ThinVec::new() + }; + let failure_code = trace.cause.as_failure_code_diag( terr, span, @@ -1995,6 +2082,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); let mut diag = self.dcx().create_err(failure_code); *diag.long_ty_path() = path; + + // Add custom notes + for note in on_type_error_notes { + diag.note(note); + } + self.note_type_err( &mut diag, &trace.cause, diff --git a/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.rs b/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.rs new file mode 100644 index 0000000000000..3b6404fa91f22 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.rs @@ -0,0 +1,19 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate will cause the +//! diagnostic to not emit the custom diagnostic message +//! +#[diagnostic::on_type_error(note = "expected `{Expected}`, found `{Found}`")] +//~^ WARN unknown diagnostic attribute +#[derive(Debug)] +struct Foo(T); + +fn takes_foo(_: Foo) {} + +fn main() { + let foo: Foo = Foo(42); + //~^ ERROR mismatched types + takes_foo(foo); + //~^ ERROR mismatched types + let bar: Foo = Foo(""); + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.stderr b/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.stderr new file mode 100644 index 0000000000000..264d3e0b90215 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/feature-gate-diagnostic-on-type-error.stderr @@ -0,0 +1,74 @@ +warning: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-type-error.rs:5:15 + | +LL | #[diagnostic::on_type_error(note = "expected `{Expected}`, found `{Found}`")] + | ^^^^^^^^^^^^^ + | + = help: add `#![feature(diagnostic_on_type_error)]` to the crate attributes to enable + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0308]: mismatched types + --> $DIR/feature-gate-diagnostic-on-type-error.rs:13:32 + | +LL | let foo: Foo = Foo(42); + | --- ^^ expected `String`, found integer + | | + | arguments to this struct are incorrect + | +help: the type constructed contains `{integer}` due to the type of the argument passed + --> $DIR/feature-gate-diagnostic-on-type-error.rs:13:28 + | +LL | let foo: Foo = Foo(42); + | ^^^^--^ + | | + | this argument influences the type of `Foo` +note: tuple struct defined here + --> $DIR/feature-gate-diagnostic-on-type-error.rs:8:8 + | +LL | struct Foo(T); + | ^^^ +help: try using a conversion method + | +LL | let foo: Foo = Foo(42.to_string()); + | ++++++++++++ + +error[E0308]: mismatched types + --> $DIR/feature-gate-diagnostic-on-type-error.rs:15:15 + | +LL | takes_foo(foo); + | --------- ^^^ expected `Foo`, found `Foo` + | | + | arguments to this function are incorrect + | + = note: expected struct `Foo` + found struct `Foo` +note: function defined here + --> $DIR/feature-gate-diagnostic-on-type-error.rs:10:4 + | +LL | fn takes_foo(_: Foo) {} + | ^^^^^^^^^ ----------- + +error[E0308]: mismatched types + --> $DIR/feature-gate-diagnostic-on-type-error.rs:17:29 + | +LL | let bar: Foo = Foo(""); + | --- ^^ expected `i32`, found `&str` + | | + | arguments to this struct are incorrect + | +help: the type constructed contains `&'static str` due to the type of the argument passed + --> $DIR/feature-gate-diagnostic-on-type-error.rs:17:25 + | +LL | let bar: Foo = Foo(""); + | ^^^^--^ + | | + | this argument influences the type of `Foo` +note: tuple struct defined here + --> $DIR/feature-gate-diagnostic-on-type-error.rs:8:8 + | +LL | struct Foo(T); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.rs b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.rs new file mode 100644 index 0000000000000..cd47d4a06a657 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.rs @@ -0,0 +1,15 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note = "expected enum `{Self}<{T}>`, found `{Found}`")] +enum MyEnum { + Variant(T), +} + +fn main() { + let e: MyEnum<&str> = "hello"; + //~^ ERROR mismatched types + //~| NOTE expected enum `MyEnum<&str>`, found `&'static str + //~| NOTE expected `MyEnum<&str>`, found `&str` + //~| NOTE expected due to this + //~| NOTE expected enum `MyEnum<&str>` +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.stderr b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.stderr new file mode 100644 index 0000000000000..79807c6121e78 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_enum.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/on_type_error_enum.rs:9:27 + | +LL | let e: MyEnum<&str> = "hello"; + | ------------ ^^^^^^^ expected `MyEnum<&str>`, found `&str` + | | + | expected due to this + | + = note: expected enum `MyEnum<&str>`, found `&'static str` + = note: expected enum `MyEnum<&str>` + found reference `&'static str` +help: try wrapping the expression in `MyEnum::Variant` + | +LL | let e: MyEnum<&str> = MyEnum::Variant("hello"); + | ++++++++++++++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.rs b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.rs new file mode 100644 index 0000000000000..73d88b2ad7d03 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_type_error)] +#[diagnostic::on_type_error(note = "expected struct `{Expected}`\n found struct `{Found}`")] +struct S(T); +struct K { + foo: T, +} +fn main() { + let s: S = S(String::new()); + //~^ ERROR mismatched types + let k: K = K { foo: "" }; + //~^ ERROR mismatched types + let _: S = k; + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.stderr b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.stderr new file mode 100644 index 0000000000000..3bb281c1de023 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_simple.stderr @@ -0,0 +1,43 @@ +error[E0308]: mismatched types + --> $DIR/on_type_error_simple.rs:8:23 + | +LL | let s: S = S(String::new()); + | - ^^^^^^^^^^^^^ expected `i32`, found `String` + | | + | arguments to this struct are incorrect + | +help: the type constructed contains `String` due to the type of the argument passed + --> $DIR/on_type_error_simple.rs:8:21 + | +LL | let s: S = S(String::new()); + | ^^-------------^ + | | + | this argument influences the type of `S` +note: tuple struct defined here + --> $DIR/on_type_error_simple.rs:3:8 + | +LL | struct S(T); + | ^ + +error[E0308]: mismatched types + --> $DIR/on_type_error_simple.rs:10:30 + | +LL | let k: K = K { foo: "" }; + | ^^ expected `i32`, found `&str` + +error[E0308]: mismatched types + --> $DIR/on_type_error_simple.rs:12:21 + | +LL | let _: S = k; + | ------ ^ expected `S`, found `K` + | | + | expected due to this + | + = note: expected struct `S` + found struct `K` + = note: expected struct `S` + found struct `K` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.rs b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.rs new file mode 100644 index 0000000000000..90474f2c1cd39 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note = "expected union `{Expected}`, found `{Found}`")] +union MyUnion { + value: std::mem::ManuallyDrop, +} + +fn takes_wrapper(_: MyUnion) {} + +fn main() { + let u1: MyUnion = 32; + //~^ ERROR mismatched types + //~| NOTE expected due to this + //~| NOTE expected `MyUnion`, found integer + //~| NOTE expected union `MyUnion`, found `{integer}` + //~| NOTE expected union `MyUnion` +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.stderr b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.stderr new file mode 100644 index 0000000000000..8ddf9246114de --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_union.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/on_type_error_union.rs:11:28 + | +LL | let u1: MyUnion = 32; + | ------------ ^^ expected `MyUnion`, found integer + | | + | expected due to this + | + = note: expected union `MyUnion`, found `{integer}` + = note: expected union `MyUnion` + found type `{integer}` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.rs b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.rs new file mode 100644 index 0000000000000..a1a402579010f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.rs @@ -0,0 +1,10 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note = "too many generics")] +struct TooMany(T, U); +//~^ WARN `#[diagnostic::on_type_error]` only supports one ADT generic parameter, but found `2` + +fn main() { + let _: TooMany = TooMany(32, "test"); + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.stderr b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.stderr new file mode 100644 index 0000000000000..49b0a2f1039f5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/on_type_error_with_too_many_generics.stderr @@ -0,0 +1,32 @@ +warning: `#[diagnostic::on_type_error]` only supports one ADT generic parameter, but found `2` + --> $DIR/on_type_error_with_too_many_generics.rs:4:15 + | +LL | struct TooMany(T, U); + | ^^^^^^ + | + = note: `#[warn(on_type_error_multiple_generics)]` on by default + +error[E0308]: mismatched types + --> $DIR/on_type_error_with_too_many_generics.rs:8:44 + | +LL | let _: TooMany = TooMany(32, "test"); + | ------- ^^^^^^ expected `i32`, found `&str` + | | + | arguments to this struct are incorrect + | +help: the type constructed contains `&'static str` due to the type of the argument passed + --> $DIR/on_type_error_with_too_many_generics.rs:8:32 + | +LL | let _: TooMany = TooMany(32, "test"); + | ^^^^^^^^^^^^------^ + | | + | this argument influences the type of `TooMany` +note: tuple struct defined here + --> $DIR/on_type_error_with_too_many_generics.rs:4:8 + | +LL | struct TooMany(T, U); + | ^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.rs b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.rs new file mode 100644 index 0000000000000..f86da600b4b30 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note = "invalid format {Expected:123}")] +//~^ WARN invalid format specifier +struct InvalidFormat1(T); + +#[diagnostic::on_type_error(note = "invalid format {Expected:!}")] +//~^ WARN invalid format specifier +struct InvalidFormat2(T); + +fn main() { + // Create type errors to trigger the notes + let _: InvalidFormat1 = InvalidFormat2(""); + //~^ ERROR mismatched types + let _: InvalidFormat2 = InvalidFormat1(3.14); + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.stderr b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.stderr new file mode 100644 index 0000000000000..2efe871d18d0e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_formats.stderr @@ -0,0 +1,44 @@ +warning: invalid format specifier + --> $DIR/report_warning_on_invalid_formats.rs:3:61 + | +LL | #[diagnostic::on_type_error(note = "invalid format {Expected:123}")] + | ^^^^ + | + = help: no format specifier are supported in this position + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: invalid format specifier + --> $DIR/report_warning_on_invalid_formats.rs:7:61 + | +LL | #[diagnostic::on_type_error(note = "invalid format {Expected:!}")] + | ^^ + | + = help: no format specifier are supported in this position + +error[E0308]: mismatched types + --> $DIR/report_warning_on_invalid_formats.rs:13:34 + | +LL | let _: InvalidFormat1 = InvalidFormat2(""); + | ------------------- ^^^^^^^^^^^^^^^^^^ expected `InvalidFormat1`, found `InvalidFormat2<&str>` + | | + | expected due to this + | + = note: invalid format InvalidFormat1 + = note: expected struct `InvalidFormat1` + found struct `InvalidFormat2<&str>` + +error[E0308]: mismatched types + --> $DIR/report_warning_on_invalid_formats.rs:15:34 + | +LL | let _: InvalidFormat2 = InvalidFormat1(3.14); + | ------------------- ^^^^^^^^^^^^^^^^^^^^ expected `InvalidFormat2`, found `InvalidFormat1<{float}>` + | | + | expected due to this + | + = note: invalid format InvalidFormat2 + = note: expected struct `InvalidFormat2` + found struct `InvalidFormat1<{float}>` + +error: aborting due to 2 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.rs b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.rs new file mode 100644 index 0000000000000..9648500acec6a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note)] +//~^ WARN malformed `diagnostic::on_type_error` attribute +struct NoValue(T); + +#[diagnostic::on_type_error(note =)] +//~^ WARN expected a literal or missing delimiter +struct EmptyValue(T); + +fn main() { + // Create type errors + let _: NoValue = 43; + //~^ ERROR mismatched types + let _: EmptyValue = 44; + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.stderr b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.stderr new file mode 100644 index 0000000000000..19ed49050ce1e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_invalid_meta_item_syntax.stderr @@ -0,0 +1,50 @@ +warning: malformed `diagnostic::on_type_error` attribute + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:3:29 + | +LL | #[diagnostic::on_type_error(note)] + | ^^^^ invalid option found here + | + = help: only `note` is allowed as option for `diagnostic::on_type_error` + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:7:28 + | +LL | #[diagnostic::on_type_error(note =)] + | ^^^^^^^^ + | + = help: only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + +error[E0308]: mismatched types + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:13:27 + | +LL | let _: NoValue = 43; + | ------------ ^^ expected `NoValue`, found integer + | | + | expected due to this + | + = note: expected struct `NoValue` + found type `{integer}` +help: try wrapping the expression in `NoValue` + | +LL | let _: NoValue = NoValue(43); + | ++++++++ + + +error[E0308]: mismatched types + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:15:30 + | +LL | let _: EmptyValue = 44; + | --------------- ^^ expected `EmptyValue`, found integer + | | + | expected due to this + | + = note: expected struct `EmptyValue` + found type `{integer}` +help: try wrapping the expression in `EmptyValue` + | +LL | let _: EmptyValue = EmptyValue(44); + | +++++++++++ + + +error: aborting due to 2 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.rs b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.rs new file mode 100644 index 0000000000000..e3f6c12f5402e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.rs @@ -0,0 +1,11 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error] +//~^ WARN missing options for `diagnostic::on_type_error` attribute +struct MissingOptions(T); + +fn main() { + // Create a type error + let _: MissingOptions = 32; + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.stderr b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.stderr new file mode 100644 index 0000000000000..e41bb27b4e3da --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_missing_options.stderr @@ -0,0 +1,27 @@ +warning: missing options for `diagnostic::on_type_error` attribute + --> $DIR/report_warning_on_missing_options.rs:3:1 + | +LL | #[diagnostic::on_type_error] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least a single `note` option is expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0308]: mismatched types + --> $DIR/report_warning_on_missing_options.rs:9:34 + | +LL | let _: MissingOptions = 32; + | ------------------- ^^ expected `MissingOptions`, found integer + | | + | expected due to this + | + = note: expected struct `MissingOptions` + found type `{integer}` +help: try wrapping the expression in `MissingOptions` + | +LL | let _: MissingOptions = MissingOptions(32); + | +++++++++++++++ + + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.rs b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.rs new file mode 100644 index 0000000000000..bfbc3fe725301 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.rs @@ -0,0 +1,38 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(note = "not an ADT")] +//~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions +fn function() {} + +#[diagnostic::on_type_error(note = "not an ADT")] +//~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions +static STATIC: i32 = 0; + +#[diagnostic::on_type_error(note = "not an ADT")] +//~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions +mod module {} + +#[diagnostic::on_type_error(note = "not an ADT")] +//~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions +trait Trait {} + +#[diagnostic::on_type_error(note = "not an ADT")] +//~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions +type TypeAlias = i32; + +struct SomeStruct; + +impl SomeStruct { + #[diagnostic::on_type_error(note = "not an ADT")] + //~^ WARN `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + fn method() {} +} + +fn main() { + // Create a type error with a valid ADT to ensure the feature works + #[diagnostic::on_type_error(note = "expected `{Expected}`, found `{Found}`")] + struct Valid(T); + + let _: Valid = SomeStruct; + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.stderr b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.stderr new file mode 100644 index 0000000000000..29a4632a48bd4 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_non_adt.stderr @@ -0,0 +1,53 @@ +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:3:1 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:7:1 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:11:1 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:15:1 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:19:1 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_type_error]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:26:5 + | +LL | #[diagnostic::on_type_error(note = "not an ADT")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/report_warning_on_non_adt.rs:36:25 + | +LL | let _: Valid = SomeStruct; + | ---------- ^^^^^^^^^^ expected `Valid`, found `SomeStruct` + | | + | expected due to this + | + = note: expected `Valid`, found `SomeStruct` + = note: expected struct `Valid` + found struct `SomeStruct` + +error: aborting due to 1 previous error; 6 warnings emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.rs b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.rs new file mode 100644 index 0000000000000..ebe680b49a59e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.rs @@ -0,0 +1,23 @@ +#![feature(diagnostic_on_type_error)] + +#[diagnostic::on_type_error(unknown = "option")] +//~^ WARN malformed `diagnostic::on_type_error` attribute +struct UnknownOption(T); + +#[diagnostic::on_type_error(message = "not allowed")] +//~^ WARN malformed `diagnostic::on_type_error` attribute +struct MessageOption(T); + +#[diagnostic::on_type_error(label = "not allowed")] +//~^ WARN malformed `diagnostic::on_type_error` attribute +struct LabelOption(T); + +fn main() { + // Create type errors + let _: UnknownOption = MessageOption(43); + //~^ ERROR mismatched types + let _: MessageOption = UnknownOption(32); + //~^ ERROR mismatched types + let _: LabelOption = UnknownOption(32); + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.stderr b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.stderr new file mode 100644 index 0000000000000..93bb09228284f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_type_error/report_warning_on_unknown_options.stderr @@ -0,0 +1,61 @@ +warning: malformed `diagnostic::on_type_error` attribute + --> $DIR/report_warning_on_unknown_options.rs:3:29 + | +LL | #[diagnostic::on_type_error(unknown = "option")] + | ^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `note` is allowed as option for `diagnostic::on_type_error` + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: malformed `diagnostic::on_type_error` attribute + --> $DIR/report_warning_on_unknown_options.rs:7:29 + | +LL | #[diagnostic::on_type_error(message = "not allowed")] + | ^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `note` is allowed as option for `diagnostic::on_type_error` + +warning: malformed `diagnostic::on_type_error` attribute + --> $DIR/report_warning_on_unknown_options.rs:11:29 + | +LL | #[diagnostic::on_type_error(label = "not allowed")] + | ^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `note` is allowed as option for `diagnostic::on_type_error` + +error[E0308]: mismatched types + --> $DIR/report_warning_on_unknown_options.rs:17:33 + | +LL | let _: UnknownOption = MessageOption(43); + | ------------------ ^^^^^^^^^^^^^^^^^ expected `UnknownOption`, found `MessageOption<{integer}>` + | | + | expected due to this + | + = note: expected struct `UnknownOption` + found struct `MessageOption<{integer}>` + +error[E0308]: mismatched types + --> $DIR/report_warning_on_unknown_options.rs:19:33 + | +LL | let _: MessageOption = UnknownOption(32); + | ------------------ ^^^^^^^^^^^^^^^^^ expected `MessageOption`, found `UnknownOption<{integer}>` + | | + | expected due to this + | + = note: expected struct `MessageOption` + found struct `UnknownOption<{integer}>` + +error[E0308]: mismatched types + --> $DIR/report_warning_on_unknown_options.rs:21:31 + | +LL | let _: LabelOption = UnknownOption(32); + | ---------------- ^^^^^^^^^^^^^^^^^ expected `LabelOption`, found `UnknownOption<{integer}>` + | | + | expected due to this + | + = note: expected struct `LabelOption` + found struct `UnknownOption<{integer}>` + +error: aborting due to 3 previous errors; 3 warnings emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs new file mode 100644 index 0000000000000..1bdbed6ce0287 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs @@ -0,0 +1,14 @@ +//! Test that the feature gate is required for diagnostic_on_type_error + +#[diagnostic::on_type_error(note = "expected {Expected}, found {Found}")] +//~^ WARN unknown diagnostic attribute +#[derive(Debug)] +struct Foo(T); + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo(String::new()); + takes_foo(foo); + //~^ERROR mismatched types +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr new file mode 100644 index 0000000000000..513802474b79b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr @@ -0,0 +1,28 @@ +warning: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-type-error.rs:3:15 + | +LL | #[diagnostic::on_type_error(note = "expected {Expected}, found {Found}")] + | ^^^^^^^^^^^^^ + | + = help: add `#![feature(diagnostic_on_type_error)]` to the crate attributes to enable + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0308]: mismatched types + --> $DIR/feature-gate-diagnostic-on-type-error.rs:12:15 + | +LL | takes_foo(foo); + | --------- ^^^ expected `Foo`, found `Foo` + | | + | arguments to this function are incorrect + | + = note: expected struct `Foo` + found struct `Foo` +note: function defined here + --> $DIR/feature-gate-diagnostic-on-type-error.rs:8:4 + | +LL | fn takes_foo(_: Foo) {} + | ^^^^^^^^^ ----------- + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`.