Skip to content

Commit 5b090d2

Browse files
committed
Introduce #[diagnostic::on_type_error(message)]
Suggested-by: Esteban Küber <esteban@kuber.com.ar> Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
1 parent e8e4541 commit 5b090d2

22 files changed

Lines changed: 392 additions & 22 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem
2323
pub(crate) mod do_not_recommend;
2424
pub(crate) mod on_const;
2525
pub(crate) mod on_move;
26+
pub(crate) mod on_type_error;
2627
pub(crate) mod on_unimplemented;
2728
pub(crate) mod on_unknown;
2829

@@ -38,6 +39,8 @@ pub(crate) enum Mode {
3839
DiagnosticOnMove,
3940
/// `#[diagnostic::on_unknown]`
4041
DiagnosticOnUnknown,
42+
/// `#[diagnostic::on_type_error]`
43+
DiagnosticOnTypeError,
4144
}
4245

4346
fn merge_directives<S: Stage>(
@@ -132,6 +135,13 @@ fn parse_directive_items<'p, S: Stage>(
132135
span,
133136
);
134137
}
138+
Mode::DiagnosticOnTypeError => {
139+
cx.emit_lint(
140+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
141+
AttributeLintKind::MalformedOnTypeErrorAttr { span },
142+
span,
143+
);
144+
}
135145
}
136146
continue;
137147
}}
@@ -149,8 +159,8 @@ fn parse_directive_items<'p, S: Stage>(
149159
match mode {
150160
Mode::RustcOnUnimplemented => {
151161
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
152-
}
153-
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknown => {
162+
},
163+
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknown | Mode::DiagnosticOnTypeError => {
154164
cx.emit_lint(
155165
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
156166
AttributeLintKind::IgnoredDiagnosticOption {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use rustc_feature::template;
2+
use rustc_hir::attrs::AttributeKind;
3+
use rustc_hir::lints::AttributeLintKind;
4+
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
5+
use rustc_span::sym;
6+
7+
use crate::attributes::diagnostic::*;
8+
use crate::attributes::prelude::*;
9+
use crate::context::{AcceptContext, Stage};
10+
use crate::parser::ArgParser;
11+
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
12+
13+
#[derive(Default)]
14+
pub(crate) struct OnTypeErrorParser {
15+
span: Option<Span>,
16+
directive: Option<(Span, Directive)>,
17+
}
18+
19+
impl OnTypeErrorParser {
20+
fn parse<'sess, S: Stage>(
21+
&mut self,
22+
cx: &mut AcceptContext<'_, 'sess, S>,
23+
args: &ArgParser,
24+
mode: Mode,
25+
) {
26+
if !cx.features().diagnostic_on_type_error() {
27+
return;
28+
}
29+
30+
let span = cx.attr_span;
31+
self.span = Some(span);
32+
33+
let Some(list) = args.list() else {
34+
cx.emit_lint(
35+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
36+
AttributeLintKind::MissingOptionsForOnTypeError,
37+
span,
38+
);
39+
return;
40+
};
41+
42+
if list.is_empty() {
43+
cx.emit_lint(
44+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
45+
AttributeLintKind::OnTypeErrorMalformedAttrExpectedLiteralOrDelimiter,
46+
list.span,
47+
);
48+
return;
49+
}
50+
51+
if let Some(directive) = parse_directive_items(cx, mode, list.mixed(), true) {
52+
merge_directives(cx, &mut self.directive, (span, directive));
53+
}
54+
}
55+
}
56+
57+
impl<S: Stage> AttributeParser<S> for OnTypeErrorParser {
58+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
59+
&[sym::diagnostic, sym::on_type_error],
60+
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
61+
|this, cx, args| {
62+
this.parse(cx, args, Mode::DiagnosticOnTypeError);
63+
},
64+
)];
65+
66+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
67+
68+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
69+
if let Some(span) = self.span {
70+
Some(AttributeKind::OnTypeError {
71+
span,
72+
directive: self.directive.map(|d| Box::new(d.1)),
73+
})
74+
} else {
75+
None
76+
}
77+
}
78+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::attributes::deprecation::*;
3131
use crate::attributes::diagnostic::do_not_recommend::*;
3232
use crate::attributes::diagnostic::on_const::*;
3333
use crate::attributes::diagnostic::on_move::*;
34+
use crate::attributes::diagnostic::on_type_error::*;
3435
use crate::attributes::diagnostic::on_unimplemented::*;
3536
use crate::attributes::diagnostic::on_unknown::*;
3637
use crate::attributes::doc::*;
@@ -154,6 +155,7 @@ attribute_parsers!(
154155
NakedParser,
155156
OnConstParser,
156157
OnMoveParser,
158+
OnTypeErrorParser,
157159
OnUnimplementedParser,
158160
OnUnknownParser,
159161
RustcAlignParser,

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,10 +509,12 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
509509
.try_report_from_nll()
510510
.or_else(|| {
511511
if let SubregionOrigin::Subtype(trace) = cause {
512+
tracing::info!("borrow checker");
512513
Some(infcx.err_ctxt().report_and_explain_type_error(
513514
*trace,
514515
infcx.tcx.param_env(generic_param_scope),
515516
TypeError::RegionsPlaceholderMismatch,
517+
None,
516518
))
517519
} else {
518520
None

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ declare_features! (
476476
(unstable, diagnostic_on_const, "1.93.0", Some(143874)),
477477
/// Allows giving on-move borrowck custom diagnostic messages for a type
478478
(unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(154181)),
479+
/// Allows giving custom types diagnostic messages on type erros
480+
(unstable, diagnostic_on_type_error, "CURRENT_RUSTC_VERSION", Some(155382)),
479481
/// Allows giving unresolved imports a custom diagnostic message
480482
(unstable, diagnostic_on_unknown, "CURRENT_RUSTC_VERSION", Some(152900)),
481483
/// Allows `#[doc(cfg(...))]`.

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,12 @@ pub enum AttributeKind {
11941194
directive: Option<Box<Directive>>,
11951195
},
11961196

1197+
/// Represents`#[diagnostic::on_type_error]`.
1198+
OnTypeError {
1199+
span: Span,
1200+
directive: Option<Box<Directive>>,
1201+
},
1202+
11971203
/// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`.
11981204
OnUnimplemented {
11991205
span: Span,

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ impl AttributeKind {
7878
NonExhaustive(..) => Yes, // Needed for rustdoc
7979
OnConst { .. } => Yes,
8080
OnMove { .. } => Yes,
81+
OnTypeError { .. } => Yes,
8182
OnUnimplemented { .. } => Yes,
8283
OnUnknown { .. } => Yes,
8384
Optimize(..) => No,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,7 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> {
19971997
),
19981998
self.param_env,
19991999
terr,
2000+
None,
20002001
);
20012002
let call_name = self.call_metadata.call_name;
20022003
err.span_label(
@@ -2116,6 +2117,7 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> {
21162117
trace,
21172118
self.arg_matching_ctxt.param_env,
21182119
*e,
2120+
None,
21192121
);
21202122
self.arg_matching_ctxt.suggest_confusable(&mut err);
21212123
reported = Some(err.emit());
@@ -2135,7 +2137,21 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> {
21352137
let (formal_ty, expected_ty) = self.formal_and_expected_inputs[expected_idx];
21362138
let (provided_ty, provided_arg_span) = self.provided_arg_tys[provided_idx];
21372139
let trace = self.mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty);
2138-
let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err);
2140+
2141+
let def_site_ty = if let Some(constructor_def_id) = self.fn_def_id {
2142+
let struct_def_id = self.tcx.parent(constructor_def_id);
2143+
let parent_ty = self.tcx.type_of(struct_def_id).skip_binder();
2144+
2145+
if let ty::Adt(_, _) = parent_ty.kind() { Some(parent_ty) } else { None }
2146+
} else {
2147+
None
2148+
};
2149+
let mut err = self.err_ctxt().report_and_explain_type_error(
2150+
trace,
2151+
self.param_env,
2152+
err,
2153+
def_site_ty,
2154+
);
21392155
self.emit_coerce_suggestions(
21402156
&mut err,
21412157
self.provided_args[provided_idx],

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,18 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
224224
&AttributeLintKind::MissingOptionsForOnUnknown => {
225225
lints::MissingOptionsForOnUnknownAttr.into_diag(dcx, level)
226226
}
227+
&AttributeLintKind::MalformedOnTypeErrorAttr { span } => {
228+
lints::MalformedOnTypeErrorAttrLint { span }.into_diag(dcx, level)
229+
}
230+
&AttributeLintKind::OnTypeErrorMalformedFormatLiterals { name } => {
231+
lints::OnTypeErrorMalformedFormatLiterals { name }.into_diag(dcx, level)
232+
}
233+
&AttributeLintKind::OnTypeErrorMalformedAttrExpectedLiteralOrDelimiter => {
234+
lints::OnTypeErrorMalformedAttrExpectedLiteralOrDelimiter.into_diag(dcx, level)
235+
}
236+
&AttributeLintKind::MissingOptionsForOnTypeError => {
237+
lints::MissingOptionsForOnTypeErrorAttr.into_diag(dcx, level)
238+
}
227239
}
228240
}
229241
}

compiler/rustc_lint/src/lints.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,6 +3603,11 @@ pub(crate) struct MissingOptionsForOnConstAttr;
36033603
#[help("at least one of the `message`, `note` and `label` options are expected")]
36043604
pub(crate) struct MissingOptionsForOnMoveAttr;
36053605

3606+
#[derive(Diagnostic)]
3607+
#[diag("missing options for `on_type_error` attribute")]
3608+
#[help("at least one of the `message`, `note` and `label` options are expected")]
3609+
pub(crate) struct MissingOptionsForOnTypeErrorAttr;
3610+
36063611
#[derive(Diagnostic)]
36073612
#[diag("malformed `on_unimplemented` attribute")]
36083613
#[help("only `message`, `note` and `label` are allowed as options")]
@@ -3642,16 +3647,40 @@ pub(crate) struct MalformedOnMoveAttrLint {
36423647
pub span: Span,
36433648
}
36443649

3650+
#[derive(Diagnostic)]
3651+
#[diag("unknown or malformed `on_type_error` attribute")]
3652+
#[help(
3653+
"only `message`, `note` and `label` are allowed as options. Their values must be string literals"
3654+
)]
3655+
pub(crate) struct MalformedOnTypeErrorAttrLint {
3656+
#[label("invalid option found here")]
3657+
pub span: Span,
3658+
}
3659+
36453660
#[derive(Diagnostic)]
36463661
#[diag("unknown parameter `{$name}`")]
36473662
#[help("expect `Self` as format argument")]
36483663
pub(crate) struct OnMoveMalformedFormatLiterals {
36493664
pub name: Symbol,
36503665
}
36513666

3667+
#[derive(Diagnostic)]
3668+
#[diag("unknown parameter `{$name}`")]
3669+
#[help("expect `Self` as format argument")]
3670+
pub(crate) struct OnTypeErrorMalformedFormatLiterals {
3671+
pub name: Symbol,
3672+
}
3673+
36523674
#[derive(Diagnostic)]
36533675
#[diag("expected a literal or missing delimiter")]
36543676
#[help(
36553677
"only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma"
36563678
)]
36573679
pub(crate) struct OnMoveMalformedAttrExpectedLiteralOrDelimiter;
3680+
3681+
#[derive(Diagnostic)]
3682+
#[diag("expected a literal or missing delimiter")]
3683+
#[help(
3684+
"only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma"
3685+
)]
3686+
pub(crate) struct OnTypeErrorMalformedAttrExpectedLiteralOrDelimiter;

0 commit comments

Comments
 (0)