Skip to content

Commit c24fb3b

Browse files
committed
on_unimplemented: split structs and implementations
1 parent 286fbe5 commit c24fb3b

8 files changed

Lines changed: 932 additions & 949 deletions

File tree

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
//! Contains the data structures used by the diagnostic attribute family.
2+
3+
use rustc_span::{DesugaringKind, Span, Symbol};
4+
5+
/// Represents a format string in a on_unimplemented attribute,
6+
/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]`
7+
#[derive(Clone, Debug)]
8+
pub struct OnUnimplementedFormatString {
9+
/// Symbol of the format string, i.e. `"content"`
10+
pub symbol: Symbol,
11+
/// The span of the format string, i.e. `"content"`
12+
pub span: Span,
13+
pub is_diagnostic_namespace_variant: bool,
14+
}
15+
16+
#[derive(Debug)]
17+
pub struct OnUnimplementedDirective {
18+
pub condition: Option<OnUnimplementedCondition>,
19+
pub subcommands: Vec<OnUnimplementedDirective>,
20+
pub message: Option<(Span, OnUnimplementedFormatString)>,
21+
pub label: Option<(Span, OnUnimplementedFormatString)>,
22+
pub notes: Vec<OnUnimplementedFormatString>,
23+
pub parent_label: Option<OnUnimplementedFormatString>,
24+
pub append_const_msg: Option<AppendConstMessage>,
25+
}
26+
27+
/// For the `#[rustc_on_unimplemented]` attribute
28+
#[derive(Default, Debug)]
29+
pub struct OnUnimplementedNote {
30+
pub message: Option<String>,
31+
pub label: Option<String>,
32+
pub notes: Vec<String>,
33+
pub parent_label: Option<String>,
34+
// If none, should fall back to a generic message
35+
pub append_const_msg: Option<AppendConstMessage>,
36+
}
37+
38+
/// Append a message for `[const] Trait` errors.
39+
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
40+
pub enum AppendConstMessage {
41+
#[default]
42+
Default,
43+
Custom(Symbol, Span),
44+
}
45+
46+
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
47+
/// either as string pieces or dynamic arguments.
48+
#[derive(Debug)]
49+
pub struct FormatString {
50+
pub input: Symbol,
51+
pub span: Span,
52+
pub pieces: Vec<Piece>,
53+
/// The formatting string was parsed successfully but with warnings
54+
pub warnings: Vec<FormatWarning>,
55+
}
56+
57+
#[derive(Debug)]
58+
pub enum Piece {
59+
Lit(String),
60+
Arg(FormatArg),
61+
}
62+
63+
#[derive(Debug)]
64+
pub enum FormatArg {
65+
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
66+
GenericParam {
67+
generic_param: Symbol,
68+
},
69+
// `{Self}`
70+
SelfUpper,
71+
/// `{This}` or `{TraitName}`
72+
This,
73+
/// The sugared form of the trait
74+
Trait,
75+
/// what we're in, like a function, method, closure etc.
76+
ItemContext,
77+
/// What the user typed, if it doesn't match anything we can use.
78+
AsIs(String),
79+
}
80+
81+
#[derive(Debug)]
82+
pub enum FormatWarning {
83+
UnknownParam { argument_name: Symbol, span: Span },
84+
PositionalArgument { span: Span, help: String },
85+
InvalidSpecifier { name: String, span: Span },
86+
FutureIncompat { span: Span, help: String },
87+
}
88+
89+
/// Represents the `on` filter in `#[rustc_on_unimplemented]`.
90+
#[derive(Debug)]
91+
pub struct OnUnimplementedCondition {
92+
pub span: Span,
93+
pub pred: Predicate,
94+
}
95+
96+
/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`].
97+
///
98+
/// It is similar to the predicate in the `cfg` attribute,
99+
/// and may contain nested predicates.
100+
#[derive(Debug)]
101+
pub enum Predicate {
102+
/// A condition like `on(crate_local)`.
103+
Flag(Flag),
104+
/// A match, like `on(Rhs = "Whatever")`.
105+
Match(NameValue),
106+
/// Negation, like `on(not($pred))`.
107+
Not(Box<Predicate>),
108+
/// True if all predicates are true, like `on(all($a, $b, $c))`.
109+
All(Vec<Predicate>),
110+
/// True if any predicate is true, like `on(any($a, $b, $c))`.
111+
Any(Vec<Predicate>),
112+
}
113+
114+
impl Predicate {
115+
pub fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
116+
match self {
117+
Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)),
118+
Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)),
119+
Predicate::Not(not) => !not.eval(eval),
120+
Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)),
121+
Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)),
122+
}
123+
}
124+
}
125+
126+
/// Represents a `MetaWord` in an `on`-filter.
127+
#[derive(Debug, Clone, Copy)]
128+
pub enum Flag {
129+
/// Whether the code causing the trait bound to not be fulfilled
130+
/// is part of the user's crate.
131+
CrateLocal,
132+
/// Whether the obligation is user-specified rather than derived.
133+
Direct,
134+
/// Whether we are in some kind of desugaring like
135+
/// `?` or `try { .. }`.
136+
FromDesugaring,
137+
}
138+
139+
/// A `MetaNameValueStr` in an `on`-filter.
140+
///
141+
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
142+
#[derive(Debug, Clone)]
143+
pub struct NameValue {
144+
pub name: Name,
145+
/// Something like `"&str"` or `"alloc::string::String"`,
146+
/// in which case it just contains a single string piece.
147+
/// But if it is something like `"&[{A}]"` then it must be formatted later.
148+
pub value: FilterFormatString,
149+
}
150+
151+
/// The valid names of the `on` filter.
152+
#[derive(Debug, Clone, Copy)]
153+
pub enum Name {
154+
Cause,
155+
FromDesugaring,
156+
SelfUpper,
157+
GenericArg(Symbol),
158+
}
159+
160+
#[derive(Debug, Clone)]
161+
pub enum FlagOrNv<'p> {
162+
Flag(&'p Flag),
163+
NameValue(&'p NameValue),
164+
}
165+
166+
/// Represents a value inside an `on` filter.
167+
///
168+
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
169+
/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`.
170+
/// The `Arg` variant is used when it contains formatting like
171+
/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`.
172+
#[derive(Debug, Clone)]
173+
pub struct FilterFormatString {
174+
pub pieces: Vec<LitOrArg>,
175+
}
176+
177+
#[derive(Debug, Clone)]
178+
pub enum LitOrArg {
179+
Lit(String),
180+
Arg(String),
181+
}
182+
183+
/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the
184+
/// [`OnUnimplementedCondition`].
185+
///
186+
/// For example, given a
187+
/// ```rust,ignore (just an example)
188+
/// #[rustc_on_unimplemented(
189+
/// on(all(from_desugaring = "QuestionMark"),
190+
/// message = "the `?` operator can only be used in {ItemContext} \
191+
/// that returns `Result` or `Option` \
192+
/// (or another type that implements `{FromResidual}`)",
193+
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
194+
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
195+
/// ),
196+
/// )]
197+
/// pub trait FromResidual<R = <Self as Try>::Residual> {
198+
/// ...
199+
/// }
200+
///
201+
/// async fn an_async_function() -> u32 {
202+
/// let x: Option<u32> = None;
203+
/// x?; //~ ERROR the `?` operator
204+
/// 22
205+
/// }
206+
/// ```
207+
/// it will look like this:
208+
///
209+
/// ```rust,ignore (just an example)
210+
/// ConditionOptions {
211+
/// self_types: ["u32", "{integral}"],
212+
/// from_desugaring: Some("QuestionMark"),
213+
/// cause: None,
214+
/// crate_local: false,
215+
/// direct: true,
216+
/// generic_args: [("Self","u32"),
217+
/// ("R", "core::option::Option<core::convert::Infallible>"),
218+
/// ("R", "core::option::Option<T>" ),
219+
/// ],
220+
/// }
221+
/// ```
222+
#[derive(Debug)]
223+
pub struct ConditionOptions {
224+
/// All the self types that may apply.
225+
pub self_types: Vec<String>,
226+
// The kind of compiler desugaring.
227+
pub from_desugaring: Option<DesugaringKind>,
228+
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode].
229+
pub cause: Option<String>,
230+
pub crate_local: bool,
231+
/// Is the obligation "directly" user-specified, rather than derived?
232+
pub direct: bool,
233+
// A list of the generic arguments and their reified types.
234+
pub generic_args: Vec<(Symbol, String)>,
235+
}
236+
237+
impl ConditionOptions {
238+
pub fn has_flag(&self, name: Flag) -> bool {
239+
match name {
240+
Flag::CrateLocal => self.crate_local,
241+
Flag::Direct => self.direct,
242+
Flag::FromDesugaring => self.from_desugaring.is_some(),
243+
}
244+
}
245+
pub fn contains(&self, name: Name, value: String) -> bool {
246+
match name {
247+
Name::SelfUpper => self.self_types.contains(&value),
248+
Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
249+
Name::Cause => self.cause == Some(value),
250+
Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
251+
}
252+
}
253+
}

compiler/rustc_hir/src/attrs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub use encode_cross_crate::EncodeCrossCrate;
99
pub use pretty_printing::PrintAttribute;
1010

1111
mod data_structures;
12+
pub mod diagnostic;
1213
mod encode_cross_crate;
1314
mod pretty_printing;
1415

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC;
2727
use rustc_span::source_map::Spanned;
2828
use rustc_target::spec::{AbiMap, AbiMapping};
2929
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
30-
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
30+
use rustc_trait_selection::error_reporting::traits::on_unimplemented::of_item_directive;
3131
use rustc_trait_selection::traits;
3232
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
3333
use tracing::{debug, instrument};
@@ -1126,7 +1126,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
11261126

11271127
pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) {
11281128
// an error would be reported if this fails.
1129-
let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id());
1129+
let _ = of_item_directive(tcx, def_id.to_def_id());
11301130
}
11311131

11321132
pub(super) fn check_specialization_validity<'tcx>(

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_errors::{
1717
Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err,
1818
};
1919
use rustc_hir::attrs::AttributeKind;
20+
use rustc_hir::attrs::diagnostic::OnUnimplementedNote;
2021
use rustc_hir::def::{CtorKind, DefKind, Res};
2122
use rustc_hir::def_id::DefId;
2223
use rustc_hir::intravisit::{self, Visitor};
@@ -38,7 +39,6 @@ use rustc_span::{
3839
kw, sym,
3940
};
4041
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
41-
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
4242
use rustc_trait_selection::infer::InferCtxtExt;
4343
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
4444
use rustc_trait_selection::traits::{

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_errors::{
1414
Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions,
1515
inline_fluent, pluralize, struct_span_code_err,
1616
};
17+
use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote};
1718
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
1819
use rustc_hir::intravisit::Visitor;
1920
use rustc_hir::{self as hir, LangItem, Node};
@@ -37,14 +38,13 @@ use rustc_span::def_id::CrateNum;
3738
use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym};
3839
use tracing::{debug, instrument};
3940

40-
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
4141
use super::suggestions::get_explanation_based_on_obligation;
4242
use super::{
4343
ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate,
4444
};
4545
use crate::error_reporting::TypeErrCtxt;
4646
use crate::error_reporting::infer::TyCategory;
47-
use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
47+
use crate::error_reporting::traits::on_unimplemented::{evaluate_directive, of_item_directive};
4848
use crate::error_reporting::traits::report_dyn_incompatibility;
4949
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn};
5050
use crate::infer::{self, InferCtxt, InferCtxtExt as _};
@@ -875,9 +875,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
875875
diag.long_ty_path(),
876876
);
877877

878-
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did)
879-
{
880-
let note = command.evaluate(
878+
if let Ok(Some(command)) = of_item_directive(self.tcx, impl_did) {
879+
let note = evaluate_directive(
880+
&command,
881881
self.tcx,
882882
predicate.skip_binder().trait_ref,
883883
&condition_options,

0 commit comments

Comments
 (0)