rustc_trait_selection/error_reporting/traits/
on_unimplemented_condition.rs1use 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#[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(input: &MetaItemInner) -> Result<Self, InvalidOnClause> {
30 let span = input.span();
31 let pred = Predicate::parse(input)?;
32 Ok(OnUnimplementedCondition { span, pred })
33 }
34}
35
36#[derive(Debug)]
41enum Predicate {
42 Flag(Flag),
44 Match(NameValue),
46 Not(Box<Predicate>),
48 All(Vec<Predicate>),
50 Any(Vec<Predicate>),
52}
53
54impl Predicate {
55 fn parse(input: &MetaItemInner) -> Result<Self, InvalidOnClause> {
56 let meta_item = match input {
57 MetaItemInner::MetaItem(meta_item) => meta_item,
58 MetaItemInner::Lit(lit) => {
59 return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span });
60 }
61 };
62
63 let Some(predicate) = meta_item.ident() else {
64 return Err(InvalidOnClause::ExpectedIdentifier {
65 span: meta_item.path.span,
66 path: meta_item.path.clone(),
67 });
68 };
69
70 match meta_item.kind {
71 MetaItemKind::List(ref mis) => match predicate.name {
72 sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis)?)),
73 sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis)?)),
74 sym::not => match &**mis {
75 [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one)?))),
76 [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot {
77 span: first.span().to(last.span()),
78 }),
79 [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }),
80 },
81 invalid_pred => {
82 Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
83 }
84 },
85 MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => {
86 let name = Name::parse(predicate);
87 let value = FilterFormatString::parse(symbol);
88 let kv = NameValue { name, value };
89 Ok(Predicate::Match(kv))
90 }
91 MetaItemKind::Word => {
92 let flag = Flag::parse(predicate)?;
93 Ok(Predicate::Flag(flag))
94 }
95 }
96 }
97
98 fn parse_sequence(sequence: &[MetaItemInner]) -> Result<Vec<Self>, InvalidOnClause> {
99 sequence.iter().map(Predicate::parse).collect()
100 }
101
102 fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
103 match self {
104 Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)),
105 Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)),
106 Predicate::Not(not) => !not.eval(eval),
107 Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)),
108 Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)),
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy)]
115enum Flag {
116 CrateLocal,
119 Direct,
121 FromDesugaring,
124}
125
126impl Flag {
127 fn parse(Ident { name, span }: Ident) -> Result<Self, InvalidOnClause> {
128 match name {
129 sym::crate_local => Ok(Flag::CrateLocal),
130 sym::direct => Ok(Flag::Direct),
131 sym::from_desugaring => Ok(Flag::FromDesugaring),
132 invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
133 }
134 }
135}
136
137#[derive(Debug, Clone)]
141struct NameValue {
142 name: Name,
143 value: FilterFormatString,
147}
148
149#[derive(Debug, Clone, Copy)]
151enum Name {
152 Cause,
153 FromDesugaring,
154 SelfUpper,
155 GenericArg(Symbol),
156}
157
158impl Name {
159 fn parse(Ident { name, .. }: Ident) -> Self {
160 match name {
161 sym::_Self | kw::SelfUpper => Name::SelfUpper,
162 sym::from_desugaring => Name::FromDesugaring,
163 sym::cause => Name::Cause,
164 generic => Name::GenericArg(generic),
167 }
168 }
169}
170
171#[derive(Debug, Clone)]
172enum FlagOrNv<'p> {
173 Flag(&'p Flag),
174 NameValue(&'p NameValue),
175}
176
177#[derive(Debug, Clone)]
184struct FilterFormatString {
185 pieces: Vec<LitOrArg>,
186}
187
188#[derive(Debug, Clone)]
189enum LitOrArg {
190 Lit(String),
191 Arg(String),
192}
193
194impl FilterFormatString {
195 fn parse(input: Symbol) -> Self {
196 let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format)
197 .map(|p| match p {
198 Piece::Lit(s) => LitOrArg::Lit(s.to_owned()),
199 Piece::NextArgument(a) => match a.position {
201 Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => {
208 LitOrArg::Lit(format!("{{{arg}}}"))
209 }
210
211 Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()),
213
214 Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")),
216 Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")),
217 },
218 })
219 .collect();
220 Self { pieces }
221 }
222
223 fn format(&self, generic_args: &[(Symbol, String)]) -> String {
224 let mut ret = String::new();
225
226 for piece in &self.pieces {
227 match piece {
228 LitOrArg::Lit(s) => ret.push_str(s),
229 LitOrArg::Arg(arg) => {
230 let s = Symbol::intern(arg);
231 match generic_args.iter().find(|(k, _)| *k == s) {
232 Some((_, val)) => ret.push_str(val),
233 None => {
234 let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}"));
237 }
238 }
239 }
240 }
241 }
242
243 ret
244 }
245}
246
247#[derive(Debug)]
287pub(crate) struct ConditionOptions {
288 pub(crate) self_types: Vec<String>,
290 pub(crate) from_desugaring: Option<DesugaringKind>,
292 pub(crate) cause: Option<String>,
294 pub(crate) crate_local: bool,
295 pub(crate) direct: bool,
297 pub(crate) generic_args: Vec<(Symbol, String)>,
299}
300
301impl ConditionOptions {
302 fn has_flag(&self, name: Flag) -> bool {
303 match name {
304 Flag::CrateLocal => self.crate_local,
305 Flag::Direct => self.direct,
306 Flag::FromDesugaring => self.from_desugaring.is_some(),
307 }
308 }
309 fn contains(&self, name: Name, value: String) -> bool {
310 match name {
311 Name::SelfUpper => self.self_types.contains(&value),
312 Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
313 Name::Cause => self.cause == Some(value),
314 Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
315 }
316 }
317}