rustc_trait_selection/error_reporting/traits/
on_unimplemented_condition.rs

1use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit};
2use rustc_parse_format::{ParseMode, Parser, Piece, Position};
3use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
4
5use crate::errors::InvalidOnClause;
6
7/// Represents the `on` filter in `#[rustc_on_unimplemented]`.
8#[derive(Debug)]
9pub(crate) struct OnUnimplementedCondition {
10    span: Span,
11    pred: Predicate,
12}
13
14impl OnUnimplementedCondition {
15    pub(crate) fn span(&self) -> Span {
16        self.span
17    }
18
19    pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool {
20        self.pred.eval(&mut |p| match p {
21            FlagOrNv::Flag(b) => options.has_flag(*b),
22            FlagOrNv::NameValue(NameValue { name, value }) => {
23                let value = value.format(&options.generic_args);
24                options.contains(*name, value)
25            }
26        })
27    }
28
29    pub(crate) fn parse(
30        input: &MetaItemInner,
31        generics: &[Symbol],
32    ) -> Result<Self, InvalidOnClause> {
33        let span = input.span();
34        let pred = Predicate::parse(input, generics)?;
35        Ok(OnUnimplementedCondition { span, pred })
36    }
37}
38
39/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`].
40///
41/// It is similar to the predicate in the `cfg` attribute,
42/// and may contain nested predicates.
43#[derive(Debug)]
44enum Predicate {
45    /// A condition like `on(crate_local)`.
46    Flag(Flag),
47    /// A match, like `on(Rhs = "Whatever")`.
48    Match(NameValue),
49    /// Negation, like `on(not($pred))`.
50    Not(Box<Predicate>),
51    /// True if all predicates are true, like `on(all($a, $b, $c))`.
52    All(Vec<Predicate>),
53    /// True if any predicate is true, like `on(any($a, $b, $c))`.
54    Any(Vec<Predicate>),
55}
56
57impl Predicate {
58    fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
59        let meta_item = match input {
60            MetaItemInner::MetaItem(meta_item) => meta_item,
61            MetaItemInner::Lit(lit) => {
62                return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span });
63            }
64        };
65
66        let Some(predicate) = meta_item.ident() else {
67            return Err(InvalidOnClause::ExpectedIdentifier {
68                span: meta_item.path.span,
69                path: meta_item.path.clone(),
70            });
71        };
72
73        match meta_item.kind {
74            MetaItemKind::List(ref mis) => match predicate.name {
75                sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)),
76                sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)),
77                sym::not => match &**mis {
78                    [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))),
79                    [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot {
80                        span: first.span().to(last.span()),
81                    }),
82                    [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }),
83                },
84                invalid_pred => {
85                    Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
86                }
87            },
88            MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => {
89                let name = Name::parse(predicate, generics)?;
90                let value = FilterFormatString::parse(symbol);
91                let kv = NameValue { name, value };
92                Ok(Predicate::Match(kv))
93            }
94            MetaItemKind::Word => {
95                let flag = Flag::parse(predicate)?;
96                Ok(Predicate::Flag(flag))
97            }
98        }
99    }
100
101    fn parse_sequence(
102        sequence: &[MetaItemInner],
103        generics: &[Symbol],
104    ) -> Result<Vec<Self>, InvalidOnClause> {
105        sequence.iter().map(|item| Predicate::parse(item, generics)).collect()
106    }
107
108    fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
109        match self {
110            Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)),
111            Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)),
112            Predicate::Not(not) => !not.eval(eval),
113            Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)),
114            Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)),
115        }
116    }
117}
118
119/// Represents a `MetaWord` in an `on`-filter.
120#[derive(Debug, Clone, Copy)]
121enum Flag {
122    /// Whether the code causing the trait bound to not be fulfilled
123    /// is part of the user's crate.
124    CrateLocal,
125    /// Whether the obligation is user-specified rather than derived.
126    Direct,
127    /// Whether we are in some kind of desugaring like
128    /// `?` or `try { .. }`.
129    FromDesugaring,
130}
131
132impl Flag {
133    fn parse(Ident { name, span }: Ident) -> Result<Self, InvalidOnClause> {
134        match name {
135            sym::crate_local => Ok(Flag::CrateLocal),
136            sym::direct => Ok(Flag::Direct),
137            sym::from_desugaring => Ok(Flag::FromDesugaring),
138            invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
139        }
140    }
141}
142
143/// A `MetaNameValueStr` in an `on`-filter.
144///
145/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
146#[derive(Debug, Clone)]
147struct NameValue {
148    name: Name,
149    /// Something like `"&str"` or `"alloc::string::String"`,
150    /// in which case it just contains a single string piece.
151    /// But if it is something like `"&[{A}]"` then it must be formatted later.
152    value: FilterFormatString,
153}
154
155/// The valid names of the `on` filter.
156#[derive(Debug, Clone, Copy)]
157enum Name {
158    Cause,
159    FromDesugaring,
160    SelfUpper,
161    GenericArg(Symbol),
162}
163
164impl Name {
165    fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
166        match name {
167            kw::SelfUpper => Ok(Name::SelfUpper),
168            sym::from_desugaring => Ok(Name::FromDesugaring),
169            sym::cause => Ok(Name::Cause),
170            generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)),
171            invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }),
172        }
173    }
174}
175
176#[derive(Debug, Clone)]
177enum FlagOrNv<'p> {
178    Flag(&'p Flag),
179    NameValue(&'p NameValue),
180}
181
182/// Represents a value inside an `on` filter.
183///
184/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
185/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`.
186/// The `Arg` variant is used when it contains formatting like
187/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`.
188#[derive(Debug, Clone)]
189struct FilterFormatString {
190    pieces: Vec<LitOrArg>,
191}
192
193#[derive(Debug, Clone)]
194enum LitOrArg {
195    Lit(String),
196    Arg(String),
197}
198
199impl FilterFormatString {
200    fn parse(input: Symbol) -> Self {
201        let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format)
202            .map(|p| match p {
203                Piece::Lit(s) => LitOrArg::Lit(s.to_owned()),
204                // We just ignore formatspecs here
205                Piece::NextArgument(a) => match a.position {
206                    // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even
207                    // if the integer type has been resolved, to allow targeting all integers.
208                    // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
209                    // from the `Display` impl of `InferTy` to be precise.
210                    //
211                    // Don't try to format these later!
212                    Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => {
213                        LitOrArg::Lit(format!("{{{arg}}}"))
214                    }
215
216                    // FIXME(mejrs) We should check if these correspond to a generic of the trait.
217                    Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()),
218
219                    // FIXME(mejrs) These should really be warnings/errors
220                    Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")),
221                    Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")),
222                },
223            })
224            .collect();
225        Self { pieces }
226    }
227
228    fn format(&self, generic_args: &[(Symbol, String)]) -> String {
229        let mut ret = String::new();
230
231        for piece in &self.pieces {
232            match piece {
233                LitOrArg::Lit(s) => ret.push_str(s),
234                LitOrArg::Arg(arg) => {
235                    let s = Symbol::intern(arg);
236                    match generic_args.iter().find(|(k, _)| *k == s) {
237                        Some((_, val)) => ret.push_str(val),
238                        None => {
239                            // FIXME(mejrs) If we start checking as mentioned in
240                            // FilterFormatString::parse then this shouldn't happen
241                            let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}"));
242                        }
243                    }
244                }
245            }
246        }
247
248        ret
249    }
250}
251
252/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the
253/// [`OnUnimplementedCondition`].
254///
255/// For example, given a
256/// ```rust,ignore (just an example)
257/// #[rustc_on_unimplemented(
258///     on(all(from_desugaring = "QuestionMark"),
259///         message = "the `?` operator can only be used in {ItemContext} \
260///                     that returns `Result` or `Option` \
261///                     (or another type that implements `{FromResidual}`)",
262///         label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
263///         parent_label = "this function should return `Result` or `Option` to accept `?`"
264///     ),
265/// )]
266/// pub trait FromResidual<R = <Self as Try>::Residual> {
267///    ...
268/// }
269///
270/// async fn an_async_function() -> u32 {
271///     let x: Option<u32> = None;
272///     x?; //~ ERROR the `?` operator
273///     22
274/// }
275///  ```
276/// it will look like this:
277///
278/// ```rust,ignore (just an example)
279/// ConditionOptions {
280///     self_types: ["u32", "{integral}"],
281///     from_desugaring: Some("QuestionMark"),
282///     cause: None,
283///     crate_local: false,
284///     direct: true,
285///     generic_args: [("Self","u32"),
286///         ("R", "core::option::Option<core::convert::Infallible>"),
287///         ("R", "core::option::Option<T>" ),
288///     ],
289/// }
290/// ```
291#[derive(Debug)]
292pub(crate) struct ConditionOptions {
293    /// All the self types that may apply.
294    pub(crate) self_types: Vec<String>,
295    // The kind of compiler desugaring.
296    pub(crate) from_desugaring: Option<DesugaringKind>,
297    /// Match on a variant of [rustc_infer::traits::ObligationCauseCode].
298    pub(crate) cause: Option<String>,
299    pub(crate) crate_local: bool,
300    /// Is the obligation "directly" user-specified, rather than derived?
301    pub(crate) direct: bool,
302    // A list of the generic arguments and their reified types.
303    pub(crate) generic_args: Vec<(Symbol, String)>,
304}
305
306impl ConditionOptions {
307    fn has_flag(&self, name: Flag) -> bool {
308        match name {
309            Flag::CrateLocal => self.crate_local,
310            Flag::Direct => self.direct,
311            Flag::FromDesugaring => self.from_desugaring.is_some(),
312        }
313    }
314    fn contains(&self, name: Name, value: String) -> bool {
315        match name {
316            Name::SelfUpper => self.self_types.contains(&value),
317            Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
318            Name::Cause => self.cause == Some(value),
319            Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
320        }
321    }
322}