1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use fmt_macros
::{Parser, Piece, Position}
;
13 use hir
::def_id
::DefId
;
14 use ty
::{self, TyCtxt}
;
15 use util
::common
::ErrorReported
;
16 use util
::nodemap
::FxHashMap
;
18 use syntax
::ast
::{MetaItem, NestedMetaItem}
;
21 use syntax_pos
::symbol
::InternedString
;
23 #[derive(Clone, Debug)]
24 pub struct OnUnimplementedFormatString(InternedString
);
27 pub struct OnUnimplementedDirective
{
28 pub condition
: Option
<MetaItem
>,
29 pub subcommands
: Vec
<OnUnimplementedDirective
>,
30 pub message
: Option
<OnUnimplementedFormatString
>,
31 pub label
: Option
<OnUnimplementedFormatString
>,
32 pub note
: Option
<OnUnimplementedFormatString
>,
35 pub struct OnUnimplementedNote
{
36 pub message
: Option
<String
>,
37 pub label
: Option
<String
>,
38 pub note
: Option
<String
>,
41 impl OnUnimplementedNote
{
42 pub fn empty() -> Self {
43 OnUnimplementedNote { message: None, label: None, note: None }
47 fn parse_error(tcx
: TyCtxt
, span
: Span
,
53 let mut diag
= struct_span_err
!(
54 tcx
.sess
, span
, E0232
, "{}", message
);
55 diag
.span_label(span
, label
);
56 if let Some(note
) = note
{
63 impl<'a
, 'gcx
, 'tcx
> OnUnimplementedDirective
{
64 pub fn parse(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
66 items
: &[NestedMetaItem
],
69 -> Result
<Self, ErrorReported
>
71 let mut errored
= false;
72 let mut item_iter
= items
.iter();
74 let condition
= if is_root
{
77 let cond
= item_iter
.next().ok_or_else(|| {
78 parse_error(tcx
, span
,
79 "empty `on`-clause in `#[rustc_on_unimplemented]`",
80 "empty on-clause here",
82 })?
.meta_item().ok_or_else(|| {
83 parse_error(tcx
, span
,
84 "invalid `on`-clause in `#[rustc_on_unimplemented]`",
85 "invalid on-clause here",
88 attr
::eval_condition(cond
, &tcx
.sess
.parse_sess
, &mut |_
| true);
92 let mut message
= None
;
95 let mut subcommands
= vec
![];
96 for item
in item_iter
{
97 if item
.check_name("message") && message
.is_none() {
98 if let Some(message_
) = item
.value_str() {
99 message
= Some(OnUnimplementedFormatString
::try_parse(
100 tcx
, trait_def_id
, message_
.as_str(), span
)?
);
103 } else if item
.check_name("label") && label
.is_none() {
104 if let Some(label_
) = item
.value_str() {
105 label
= Some(OnUnimplementedFormatString
::try_parse(
106 tcx
, trait_def_id
, label_
.as_str(), span
)?
);
109 } else if item
.check_name("note") && note
.is_none() {
110 if let Some(note_
) = item
.value_str() {
111 note
= Some(OnUnimplementedFormatString
::try_parse(
112 tcx
, trait_def_id
, note_
.as_str(), span
)?
);
115 } else if item
.check_name("on") && is_root
&&
116 message
.is_none() && label
.is_none() && note
.is_none()
118 if let Some(items
) = item
.meta_item_list() {
119 if let Ok(subcommand
) =
120 Self::parse(tcx
, trait_def_id
, &items
, item
.span
, false)
122 subcommands
.push(subcommand
);
131 parse_error(tcx
, item
.span
,
132 "this attribute must have a valid value",
133 "expected value here",
134 Some(r
#"eg `#[rustc_on_unimplemented = "foo"]`"#));
140 Ok(OnUnimplementedDirective { condition, message, label, subcommands, note }
)
145 pub fn of_item(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
148 -> Result
<Option
<Self>, ErrorReported
>
150 let attrs
= tcx
.get_attrs(impl_def_id
);
152 let attr
= if let Some(item
) = attr
::find_by_name(&attrs
, "rustc_on_unimplemented") {
158 let result
= if let Some(items
) = attr
.meta_item_list() {
159 Self::parse(tcx
, trait_def_id
, &items
, attr
.span
, true).map(Some
)
160 } else if let Some(value
) = attr
.value_str() {
161 Ok(Some(OnUnimplementedDirective
{
165 label
: Some(OnUnimplementedFormatString
::try_parse(
166 tcx
, trait_def_id
, value
.as_str(), attr
.span
)?
),
170 return Err(parse_error(tcx
, attr
.span
,
171 "`#[rustc_on_unimplemented]` requires a value",
172 "value required here",
173 Some(r
#"eg `#[rustc_on_unimplemented = "foo"]`"#)));
175 debug
!("of_item({:?}/{:?}) = {:?}", trait_def_id
, impl_def_id
, result
);
179 pub fn evaluate(&self,
180 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
181 trait_ref
: ty
::TraitRef
<'tcx
>,
182 options
: &[(String
, Option
<String
>)])
183 -> OnUnimplementedNote
185 let mut message
= None
;
186 let mut label
= None
;
188 info
!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref
, options
);
190 for command
in self.subcommands
.iter().chain(Some(self)).rev() {
191 if let Some(ref condition
) = command
.condition
{
192 if !attr
::eval_condition(condition
, &tcx
.sess
.parse_sess
, &mut |c
| {
193 options
.contains(&(c
.name().as_str().to_string(),
194 match c
.value_str().map(|s
| s
.as_str().to_string()) {
199 debug
!("evaluate: skipping {:?} due to condition", command
);
203 debug
!("evaluate: {:?} succeeded", command
);
204 if let Some(ref message_
) = command
.message
{
205 message
= Some(message_
.clone());
208 if let Some(ref label_
) = command
.label
{
209 label
= Some(label_
.clone());
212 if let Some(ref note_
) = command
.note
{
213 note
= Some(note_
.clone());
217 OnUnimplementedNote
{
218 label
: label
.map(|l
| l
.format(tcx
, trait_ref
)),
219 message
: message
.map(|m
| m
.format(tcx
, trait_ref
)),
220 note
: note
.map(|n
| n
.format(tcx
, trait_ref
)),
225 impl<'a
, 'gcx
, 'tcx
> OnUnimplementedFormatString
{
226 pub fn try_parse(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
228 from
: InternedString
,
230 -> Result
<Self, ErrorReported
>
232 let result
= OnUnimplementedFormatString(from
);
233 result
.verify(tcx
, trait_def_id
, err_sp
)?
;
238 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
241 -> Result
<(), ErrorReported
>
243 let name
= tcx
.item_name(trait_def_id
);
244 let generics
= tcx
.generics_of(trait_def_id
);
245 let parser
= Parser
::new(&self.0);
246 let types
= &generics
.types
;
247 let mut result
= Ok(());
248 for token
in parser
{
250 Piece
::String(_
) => (), // Normal string, no need to check it
251 Piece
::NextArgument(a
) => match a
.position
{
252 // `{Self}` is allowed
253 Position
::ArgumentNamed(s
) if s
== "Self" => (),
254 // `{ThisTraitsName}` is allowed
255 Position
::ArgumentNamed(s
) if s
== name
=> (),
256 // So is `{A}` if A is a type parameter
257 Position
::ArgumentNamed(s
) => match types
.iter().find(|t
| {
262 span_err
!(tcx
.sess
, span
, E0230
,
263 "there is no type parameter \
266 result
= Err(ErrorReported
);
269 // `{:1}` and `{}` are not to be used
270 Position
::ArgumentIs(_
) | Position
::ArgumentImplicitlyIs(_
) => {
271 span_err
!(tcx
.sess
, span
, E0231
,
272 "only named substitution \
273 parameters are allowed");
274 result
= Err(ErrorReported
);
284 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
285 trait_ref
: ty
::TraitRef
<'tcx
>)
288 let name
= tcx
.item_name(trait_ref
.def_id
);
289 let trait_str
= tcx
.item_path_str(trait_ref
.def_id
);
290 let generics
= tcx
.generics_of(trait_ref
.def_id
);
291 let generic_map
= generics
.types
.iter().map(|param
| {
292 (param
.name
.as_str().to_string(),
293 trait_ref
.substs
.type_for_def(param
).to_string())
294 }).collect
::<FxHashMap
<String
, String
>>();
296 let parser
= Parser
::new(&self.0);
299 Piece
::String(s
) => s
,
300 Piece
::NextArgument(a
) => match a
.position
{
301 Position
::ArgumentNamed(s
) => match generic_map
.get(s
) {
303 None
if s
== name
=> {
307 bug
!("broken on_unimplemented {:?} for {:?}: \
308 no argument matching {:?}",
309 self.0, trait_ref
, s
)
313 bug
!("broken on_unimplemented {:?} - bad \