|
| 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 | +} |
0 commit comments