1 use rustc_ast
::{MetaItem, NestedMetaItem}
;
2 use rustc_attr
as attr
;
3 use rustc_data_structures
::fx
::FxHashMap
;
4 use rustc_errors
::{struct_span_err, ErrorGuaranteed}
;
5 use rustc_hir
::def_id
::DefId
;
6 use rustc_middle
::ty
::{self, GenericParamDefKind, TyCtxt}
;
7 use rustc_parse_format
::{ParseMode, Parser, Piece, Position}
;
8 use rustc_span
::symbol
::{kw, sym, Symbol}
;
9 use rustc_span
::{Span, DUMMY_SP}
;
11 #[derive(Clone, Debug)]
12 pub struct OnUnimplementedFormatString(Symbol
);
15 pub struct OnUnimplementedDirective
{
16 pub condition
: Option
<MetaItem
>,
17 pub subcommands
: Vec
<OnUnimplementedDirective
>,
18 pub message
: Option
<OnUnimplementedFormatString
>,
19 pub label
: Option
<OnUnimplementedFormatString
>,
20 pub note
: Option
<OnUnimplementedFormatString
>,
21 pub enclosing_scope
: Option
<OnUnimplementedFormatString
>,
22 pub append_const_msg
: Option
<Option
<Symbol
>>,
26 pub struct OnUnimplementedNote
{
27 pub message
: Option
<String
>,
28 pub label
: Option
<String
>,
29 pub note
: Option
<String
>,
30 pub enclosing_scope
: Option
<String
>,
31 /// Append a message for `~const Trait` errors. `None` means not requested and
32 /// should fallback to a generic message, `Some(None)` suggests using the default
33 /// appended message, `Some(Some(s))` suggests use the `s` message instead of the
35 pub append_const_msg
: Option
<Option
<Symbol
>>,
44 ) -> ErrorGuaranteed
{
45 let mut diag
= struct_span_err
!(tcx
.sess
, span
, E0232
, "{}", message
);
46 diag
.span_label(span
, label
);
47 if let Some(note
) = note
{
53 impl<'tcx
> OnUnimplementedDirective
{
57 items
: &[NestedMetaItem
],
60 ) -> Result
<Self, ErrorGuaranteed
> {
61 let mut errored
= None
;
62 let mut item_iter
= items
.iter();
64 let parse_value
= |value_str
| {
65 OnUnimplementedFormatString
::try_parse(tcx
, item_def_id
, value_str
, span
).map(Some
)
68 let condition
= if is_root
{
77 "empty `on`-clause in `#[rustc_on_unimplemented]`",
78 "empty on-clause here",
87 "invalid `on`-clause in `#[rustc_on_unimplemented]`",
88 "invalid on-clause here",
92 attr
::eval_condition(cond
, &tcx
.sess
.parse_sess
, Some(tcx
.features()), &mut |item
| {
93 if let Some(symbol
) = item
.value_str() && let Err(guar
) = parse_value(symbol
) {
101 let mut message
= None
;
102 let mut label
= None
;
104 let mut enclosing_scope
= None
;
105 let mut subcommands
= vec
![];
106 let mut append_const_msg
= None
;
108 for item
in item_iter
{
109 if item
.has_name(sym
::message
) && message
.is_none() {
110 if let Some(message_
) = item
.value_str() {
111 message
= parse_value(message_
)?
;
114 } else if item
.has_name(sym
::label
) && label
.is_none() {
115 if let Some(label_
) = item
.value_str() {
116 label
= parse_value(label_
)?
;
119 } else if item
.has_name(sym
::note
) && note
.is_none() {
120 if let Some(note_
) = item
.value_str() {
121 note
= parse_value(note_
)?
;
124 } else if item
.has_name(sym
::enclosing_scope
) && enclosing_scope
.is_none() {
125 if let Some(enclosing_scope_
) = item
.value_str() {
126 enclosing_scope
= parse_value(enclosing_scope_
)?
;
129 } else if item
.has_name(sym
::on
)
135 if let Some(items
) = item
.meta_item_list() {
136 match Self::parse(tcx
, item_def_id
, &items
, item
.span(), false) {
137 Ok(subcommand
) => subcommands
.push(subcommand
),
138 Err(reported
) => errored
= Some(reported
),
142 } else if item
.has_name(sym
::append_const_msg
) && append_const_msg
.is_none() {
143 if let Some(msg
) = item
.value_str() {
144 append_const_msg
= Some(Some(msg
));
146 } else if item
.is_word() {
147 append_const_msg
= Some(None
);
156 "this attribute must have a valid value",
157 "expected value here",
158 Some(r
#"eg `#[rustc_on_unimplemented(message="foo")]`"#),
162 if let Some(reported
) = errored
{
165 Ok(OnUnimplementedDirective
{
177 pub fn of_item(tcx
: TyCtxt
<'tcx
>, item_def_id
: DefId
) -> Result
<Option
<Self>, ErrorGuaranteed
> {
178 let Some(attr
) = tcx
.get_attr(item_def_id
, sym
::rustc_on_unimplemented
) else {
182 let result
= if let Some(items
) = attr
.meta_item_list() {
183 Self::parse(tcx
, item_def_id
, &items
, attr
.span
, true).map(Some
)
184 } else if let Some(value
) = attr
.value_str() {
185 Ok(Some(OnUnimplementedDirective
{
189 label
: Some(OnUnimplementedFormatString
::try_parse(
196 enclosing_scope
: None
,
197 append_const_msg
: None
,
201 tcx
.sess
.delay_span_bug(DUMMY_SP
, "of_item: neither meta_item_list nor value_str");
202 return Err(reported
);
204 debug
!("of_item({:?}) = {:?}", item_def_id
, result
);
211 trait_ref
: ty
::TraitRef
<'tcx
>,
212 options
: &[(Symbol
, Option
<String
>)],
213 ) -> OnUnimplementedNote
{
214 let mut message
= None
;
215 let mut label
= None
;
217 let mut enclosing_scope
= None
;
218 let mut append_const_msg
= None
;
219 info
!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref
, options
);
221 let options_map
: FxHashMap
<Symbol
, String
> =
222 options
.iter().filter_map(|(k
, v
)| v
.as_ref().map(|v
| (*k
, v
.to_owned()))).collect();
224 for command
in self.subcommands
.iter().chain(Some(self)).rev() {
225 if let Some(ref condition
) = command
.condition
&& !attr
::eval_condition(
227 &tcx
.sess
.parse_sess
,
228 Some(tcx
.features()),
230 c
.ident().map_or(false, |ident
| {
231 let value
= c
.value_str().map(|s
| {
232 OnUnimplementedFormatString(s
).format(tcx
, trait_ref
, &options_map
)
235 options
.contains(&(ident
.name
, value
))
239 debug
!("evaluate: skipping {:?} due to condition", command
);
242 debug
!("evaluate: {:?} succeeded", command
);
243 if let Some(ref message_
) = command
.message
{
244 message
= Some(message_
.clone());
247 if let Some(ref label_
) = command
.label
{
248 label
= Some(label_
.clone());
251 if let Some(ref note_
) = command
.note
{
252 note
= Some(note_
.clone());
255 if let Some(ref enclosing_scope_
) = command
.enclosing_scope
{
256 enclosing_scope
= Some(enclosing_scope_
.clone());
259 append_const_msg
= command
.append_const_msg
;
262 OnUnimplementedNote
{
263 label
: label
.map(|l
| l
.format(tcx
, trait_ref
, &options_map
)),
264 message
: message
.map(|m
| m
.format(tcx
, trait_ref
, &options_map
)),
265 note
: note
.map(|n
| n
.format(tcx
, trait_ref
, &options_map
)),
266 enclosing_scope
: enclosing_scope
.map(|e_s
| e_s
.format(tcx
, trait_ref
, &options_map
)),
272 impl<'tcx
> OnUnimplementedFormatString
{
278 ) -> Result
<Self, ErrorGuaranteed
> {
279 let result
= OnUnimplementedFormatString(from
);
280 result
.verify(tcx
, item_def_id
, err_sp
)?
;
289 ) -> Result
<(), ErrorGuaranteed
> {
290 let trait_def_id
= if tcx
.is_trait(item_def_id
) {
293 tcx
.trait_id_of_impl(item_def_id
)
294 .expect("expected `on_unimplemented` to correspond to a trait")
296 let trait_name
= tcx
.item_name(trait_def_id
);
297 let generics
= tcx
.generics_of(item_def_id
);
298 let s
= self.0.as_str();
299 let parser
= Parser
::new(s
, None
, None
, false, ParseMode
::Format
);
300 let mut result
= Ok(());
301 for token
in parser
{
303 Piece
::String(_
) => (), // Normal string, no need to check it
304 Piece
::NextArgument(a
) => match a
.position
{
305 Position
::ArgumentNamed(s
, _
) => {
306 match Symbol
::intern(s
) {
307 // `{Self}` is allowed
309 // `{ThisTraitsName}` is allowed
310 s
if s
== trait_name
=> (),
311 // `{from_method}` is allowed
312 sym
::from_method
=> (),
313 // `{from_desugaring}` is allowed
314 sym
::from_desugaring
=> (),
315 // `{ItemContext}` is allowed
316 sym
::ItemContext
=> (),
317 // `{integral}` and `{integer}` and `{float}` are allowed
318 sym
::integral
| sym
::integer_
| sym
::float
=> (),
319 // So is `{A}` if A is a type parameter
320 s
=> match generics
.params
.iter().find(|param
| param
.name
== s
) {
323 let reported
= struct_span_err
!(
327 "there is no parameter `{}` on {}",
329 if trait_def_id
== item_def_id
{
330 format
!("trait `{}`", trait_name
)
336 result
= Err(reported
);
341 // `{:1}` and `{}` are not to be used
342 Position
::ArgumentIs(_
) | Position
::ArgumentImplicitlyIs(_
) => {
343 let reported
= struct_span_err
!(
347 "only named substitution parameters are allowed"
350 result
= Err(reported
);
362 trait_ref
: ty
::TraitRef
<'tcx
>,
363 options
: &FxHashMap
<Symbol
, String
>,
365 let name
= tcx
.item_name(trait_ref
.def_id
);
366 let trait_str
= tcx
.def_path_str(trait_ref
.def_id
);
367 let generics
= tcx
.generics_of(trait_ref
.def_id
);
368 let generic_map
= generics
371 .filter_map(|param
| {
372 let value
= match param
.kind
{
373 GenericParamDefKind
::Type { .. }
| GenericParamDefKind
::Const { .. }
=> {
374 trait_ref
.substs
[param
.index
as usize].to_string()
376 GenericParamDefKind
::Lifetime
=> return None
,
378 let name
= param
.name
;
381 .collect
::<FxHashMap
<Symbol
, String
>>();
382 let empty_string
= String
::new();
384 let s
= self.0.as_str();
385 let parser
= Parser
::new(s
, None
, None
, false, ParseMode
::Format
);
386 let item_context
= (options
.get(&sym
::ItemContext
)).unwrap_or(&empty_string
);
389 Piece
::String(s
) => s
,
390 Piece
::NextArgument(a
) => match a
.position
{
391 Position
::ArgumentNamed(s
, _
) => {
392 let s
= Symbol
::intern(s
);
393 match generic_map
.get(&s
) {
395 None
if s
== name
=> &trait_str
,
397 if let Some(val
) = options
.get(&s
) {
399 } else if s
== sym
::from_desugaring
|| s
== sym
::from_method
{
400 // don't break messages using these two arguments incorrectly
402 } else if s
== sym
::ItemContext
{
404 } else if s
== sym
::integral
{
406 } else if s
== sym
::integer_
{
408 } else if s
== sym
::float
{
412 "broken on_unimplemented {:?} for {:?}: \
413 no argument matching {:?}",
422 _
=> bug
!("broken on_unimplemented {:?} - bad format arg", self.0),