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, ErrorReported}
;
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}
;
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
>,
25 pub struct OnUnimplementedNote
{
26 pub message
: Option
<String
>,
27 pub label
: Option
<String
>,
28 pub note
: Option
<String
>,
29 pub enclosing_scope
: Option
<String
>,
39 let mut diag
= struct_span_err
!(tcx
.sess
, span
, E0232
, "{}", message
);
40 diag
.span_label(span
, label
);
41 if let Some(note
) = note
{
48 impl<'tcx
> OnUnimplementedDirective
{
52 items
: &[NestedMetaItem
],
55 ) -> Result
<Self, ErrorReported
> {
56 let mut errored
= false;
57 let mut item_iter
= items
.iter();
59 let condition
= if is_root
{
68 "empty `on`-clause in `#[rustc_on_unimplemented]`",
69 "empty on-clause here",
78 "invalid `on`-clause in `#[rustc_on_unimplemented]`",
79 "invalid on-clause here",
83 attr
::eval_condition(cond
, &tcx
.sess
.parse_sess
, Some(tcx
.features()), &mut |_
| true);
87 let mut message
= None
;
90 let mut enclosing_scope
= None
;
91 let mut subcommands
= vec
![];
93 let parse_value
= |value_str
| {
94 OnUnimplementedFormatString
::try_parse(tcx
, trait_def_id
, value_str
, span
).map(Some
)
97 for item
in item_iter
{
98 if item
.has_name(sym
::message
) && message
.is_none() {
99 if let Some(message_
) = item
.value_str() {
100 message
= parse_value(message_
)?
;
103 } else if item
.has_name(sym
::label
) && label
.is_none() {
104 if let Some(label_
) = item
.value_str() {
105 label
= parse_value(label_
)?
;
108 } else if item
.has_name(sym
::note
) && note
.is_none() {
109 if let Some(note_
) = item
.value_str() {
110 note
= parse_value(note_
)?
;
113 } else if item
.has_name(sym
::enclosing_scope
) && enclosing_scope
.is_none() {
114 if let Some(enclosing_scope_
) = item
.value_str() {
115 enclosing_scope
= parse_value(enclosing_scope_
)?
;
118 } else if item
.has_name(sym
::on
)
124 if let Some(items
) = item
.meta_item_list() {
125 if let Ok(subcommand
) =
126 Self::parse(tcx
, trait_def_id
, &items
, item
.span(), false)
128 subcommands
.push(subcommand
);
140 "this attribute must have a valid value",
141 "expected value here",
142 Some(r
#"eg `#[rustc_on_unimplemented(message="foo")]`"#),
149 Ok(OnUnimplementedDirective
{
164 ) -> Result
<Option
<Self>, ErrorReported
> {
165 let attrs
= tcx
.get_attrs(impl_def_id
);
167 let Some(attr
) = tcx
.sess
.find_by_name(&attrs
, sym
::rustc_on_unimplemented
) else {
171 let result
= if let Some(items
) = attr
.meta_item_list() {
172 Self::parse(tcx
, trait_def_id
, &items
, attr
.span
, true).map(Some
)
173 } else if let Some(value
) = attr
.value_str() {
174 Ok(Some(OnUnimplementedDirective
{
178 label
: Some(OnUnimplementedFormatString
::try_parse(
185 enclosing_scope
: None
,
188 return Err(ErrorReported
);
190 debug
!("of_item({:?}/{:?}) = {:?}", trait_def_id
, impl_def_id
, result
);
197 trait_ref
: ty
::TraitRef
<'tcx
>,
198 options
: &[(Symbol
, Option
<String
>)],
199 ) -> OnUnimplementedNote
{
200 let mut message
= None
;
201 let mut label
= None
;
203 let mut enclosing_scope
= None
;
204 info
!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref
, options
);
206 for command
in self.subcommands
.iter().chain(Some(self)).rev() {
207 if let Some(ref condition
) = command
.condition
{
208 if !attr
::eval_condition(
210 &tcx
.sess
.parse_sess
,
211 Some(tcx
.features()),
213 c
.ident().map_or(false, |ident
| {
214 options
.contains(&(ident
.name
, c
.value_str().map(|s
| s
.to_string())))
218 debug
!("evaluate: skipping {:?} due to condition", command
);
222 debug
!("evaluate: {:?} succeeded", command
);
223 if let Some(ref message_
) = command
.message
{
224 message
= Some(message_
.clone());
227 if let Some(ref label_
) = command
.label
{
228 label
= Some(label_
.clone());
231 if let Some(ref note_
) = command
.note
{
232 note
= Some(note_
.clone());
235 if let Some(ref enclosing_scope_
) = command
.enclosing_scope
{
236 enclosing_scope
= Some(enclosing_scope_
.clone());
240 let options
: FxHashMap
<Symbol
, String
> =
241 options
.iter().filter_map(|(k
, v
)| v
.as_ref().map(|v
| (*k
, v
.to_owned()))).collect();
242 OnUnimplementedNote
{
243 label
: label
.map(|l
| l
.format(tcx
, trait_ref
, &options
)),
244 message
: message
.map(|m
| m
.format(tcx
, trait_ref
, &options
)),
245 note
: note
.map(|n
| n
.format(tcx
, trait_ref
, &options
)),
246 enclosing_scope
: enclosing_scope
.map(|e_s
| e_s
.format(tcx
, trait_ref
, &options
)),
251 impl<'tcx
> OnUnimplementedFormatString
{
257 ) -> Result
<Self, ErrorReported
> {
258 let result
= OnUnimplementedFormatString(from
);
259 result
.verify(tcx
, trait_def_id
, err_sp
)?
;
268 ) -> Result
<(), ErrorReported
> {
269 let name
= tcx
.item_name(trait_def_id
);
270 let generics
= tcx
.generics_of(trait_def_id
);
271 let s
= self.0.as_str();
272 let parser
= Parser
::new(s
, None
, None
, false, ParseMode
::Format
);
273 let mut result
= Ok(());
274 for token
in parser
{
276 Piece
::String(_
) => (), // Normal string, no need to check it
277 Piece
::NextArgument(a
) => match a
.position
{
278 // `{Self}` is allowed
279 Position
::ArgumentNamed(s
) if s
== kw
::SelfUpper
=> (),
280 // `{ThisTraitsName}` is allowed
281 Position
::ArgumentNamed(s
) if s
== name
=> (),
282 // `{from_method}` is allowed
283 Position
::ArgumentNamed(s
) if s
== sym
::from_method
=> (),
284 // `{from_desugaring}` is allowed
285 Position
::ArgumentNamed(s
) if s
== sym
::from_desugaring
=> (),
286 // `{ItemContext}` is allowed
287 Position
::ArgumentNamed(s
) if s
== sym
::ItemContext
=> (),
288 // So is `{A}` if A is a type parameter
289 Position
::ArgumentNamed(s
) => {
290 match generics
.params
.iter().find(|param
| param
.name
== s
) {
297 "there is no parameter `{}` on trait `{}`",
302 result
= Err(ErrorReported
);
306 // `{:1}` and `{}` are not to be used
307 Position
::ArgumentIs(_
) | Position
::ArgumentImplicitlyIs(_
) => {
312 "only named substitution parameters are allowed"
315 result
= Err(ErrorReported
);
327 trait_ref
: ty
::TraitRef
<'tcx
>,
328 options
: &FxHashMap
<Symbol
, String
>,
330 let name
= tcx
.item_name(trait_ref
.def_id
);
331 let trait_str
= tcx
.def_path_str(trait_ref
.def_id
);
332 let generics
= tcx
.generics_of(trait_ref
.def_id
);
333 let generic_map
= generics
336 .filter_map(|param
| {
337 let value
= match param
.kind
{
338 GenericParamDefKind
::Type { .. }
| GenericParamDefKind
::Const { .. }
=> {
339 trait_ref
.substs
[param
.index
as usize].to_string()
341 GenericParamDefKind
::Lifetime
=> return None
,
343 let name
= param
.name
;
346 .collect
::<FxHashMap
<Symbol
, String
>>();
347 let empty_string
= String
::new();
349 let s
= self.0.as_str();
350 let parser
= Parser
::new(s
, None
, None
, false, ParseMode
::Format
);
351 let item_context
= (options
.get(&sym
::ItemContext
)).unwrap_or(&empty_string
);
354 Piece
::String(s
) => s
,
355 Piece
::NextArgument(a
) => match a
.position
{
356 Position
::ArgumentNamed(s
) => match generic_map
.get(&s
) {
358 None
if s
== name
=> &trait_str
,
360 if let Some(val
) = options
.get(&s
) {
362 } else if s
== sym
::from_desugaring
|| s
== sym
::from_method
{
363 // don't break messages using these two arguments incorrectly
365 } else if s
== sym
::ItemContext
{
369 "broken on_unimplemented {:?} for {:?}: \
370 no argument matching {:?}",
378 _
=> bug
!("broken on_unimplemented {:?} - bad format arg", self.0),