Skip to content

Commit 6866aea

Browse files
committed
implement improved on_unimplemented directives
1 parent cf07ebd commit 6866aea

File tree

9 files changed

+290
-50
lines changed

9 files changed

+290
-50
lines changed

src/librustc/diagnostics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,9 +2012,9 @@ register_diagnostics! {
20122012
// E0102, // replaced with E0282
20132013
// E0134,
20142014
// E0135,
2015-
// E0271, // on_unimplemented #0
2016-
// E0272, // on_unimplemented #1
2017-
// E0273, // on_unimplemented #2
2015+
// E0272, // on_unimplemented #0
2016+
// E0273, // on_unimplemented #1
2017+
// E0274, // on_unimplemented #2
20182018
E0278, // requirement is not satisfied
20192019
E0279, // requirement is not satisfied
20202020
E0280, // requirement is not satisfied

src/librustc/traits/error_reporting.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use super::{
1515
Obligation,
1616
ObligationCause,
1717
ObligationCauseCode,
18-
OnUnimplementedInfo,
18+
OnUnimplementedDirective,
19+
OnUnimplementedNote,
1920
OutputTypeParameterMismatch,
2021
TraitNotObjectSafe,
2122
PredicateObligation,
@@ -316,18 +317,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
316317
}
317318
}
318319

319-
fn on_unimplemented_note(&self,
320-
trait_ref: ty::PolyTraitRef<'tcx>,
321-
obligation: &PredicateObligation<'tcx>) -> Option<String> {
320+
fn on_unimplemented_note(
321+
&self,
322+
trait_ref: ty::PolyTraitRef<'tcx>,
323+
obligation: &PredicateObligation<'tcx>) ->
324+
OnUnimplementedNote
325+
{
322326
let def_id = self.impl_similar_to(trait_ref, obligation)
323327
.unwrap_or(trait_ref.def_id());
324-
let trait_ref = trait_ref.skip_binder();
328+
let trait_ref = *trait_ref.skip_binder();
325329

326-
match OnUnimplementedInfo::of_item(
327-
self.tcx, trait_ref.def_id, def_id, obligation.cause.span
330+
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(
331+
self.tcx, trait_ref.def_id, def_id
328332
) {
329-
Ok(Some(info)) => Some(info.label.format(self.tcx, *trait_ref)),
330-
_ => None
333+
command.evaluate(self.tcx, trait_ref, &[])
334+
} else {
335+
OnUnimplementedNote::empty()
331336
}
332337
}
333338

@@ -519,17 +524,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
519524
let (post_message, pre_message) =
520525
self.get_parent_trait_ref(&obligation.cause.code)
521526
.map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
522-
.unwrap_or((String::new(), String::new()));
527+
.unwrap_or((String::new(), String::new()));
528+
529+
let OnUnimplementedNote { message, label }
530+
= self.on_unimplemented_note(trait_ref, obligation);
531+
let have_alt_message = message.is_some() || label.is_some();
532+
523533
let mut err = struct_span_err!(
524534
self.tcx.sess,
525535
span,
526536
E0277,
527-
"the trait bound `{}` is not satisfied{}",
528-
trait_ref.to_predicate(),
529-
post_message);
537+
"{}",
538+
message.unwrap_or_else(|| {
539+
format!("the trait bound `{}` is not satisfied{}",
540+
trait_ref.to_predicate(), post_message)
541+
}));
530542

531-
let unimplemented_note = self.on_unimplemented_note(trait_ref, obligation);
532-
if let Some(ref s) = unimplemented_note {
543+
if let Some(ref s) = label {
533544
// If it has a custom "#[rustc_on_unimplemented]"
534545
// error message, let's display it as the label!
535546
err.span_label(span, s.as_str());
@@ -557,7 +568,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
557568
// which is somewhat confusing.
558569
err.help(&format!("consider adding a `where {}` bound",
559570
trait_ref.to_predicate()));
560-
} else if unimplemented_note.is_none() {
571+
} else if !have_alt_message {
561572
// Can't show anything else useful, try to find similar impls.
562573
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
563574
self.report_similar_impl_candidates(impl_candidates, &mut err);

src/librustc/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub use self::project::{normalize, normalize_projection_type, Normalized};
3737
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal};
3838
pub use self::object_safety::ObjectSafetyViolation;
3939
pub use self::object_safety::MethodViolationCode;
40-
pub use self::on_unimplemented::OnUnimplementedInfo;
40+
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
4141
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
4242
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
4343
pub use self::specialize::{SpecializesCache, find_associated_item};

src/librustc/traits/on_unimplemented.rs

Lines changed: 157 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,127 @@ use ty::{self, TyCtxt};
1515
use util::common::ErrorReported;
1616
use util::nodemap::FxHashMap;
1717

18+
use syntax::ast::{MetaItem, NestedMetaItem};
19+
use syntax::attr;
1820
use syntax_pos::Span;
1921
use syntax_pos::symbol::InternedString;
2022

23+
#[derive(Clone, Debug)]
2124
pub struct OnUnimplementedFormatString(InternedString);
22-
pub struct OnUnimplementedInfo {
23-
pub label: OnUnimplementedFormatString
25+
26+
#[derive(Debug)]
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+
}
33+
34+
pub struct OnUnimplementedNote {
35+
pub message: Option<String>,
36+
pub label: Option<String>,
37+
}
38+
39+
impl OnUnimplementedNote {
40+
pub fn empty() -> Self {
41+
OnUnimplementedNote { message: None, label: None }
42+
}
2443
}
2544

26-
impl<'a, 'gcx, 'tcx> OnUnimplementedInfo {
45+
fn parse_error(tcx: TyCtxt, span: Span,
46+
message: &str,
47+
label: &str,
48+
note: Option<&str>)
49+
-> ErrorReported
50+
{
51+
let mut diag = struct_span_err!(
52+
tcx.sess, span, E0232, "{}", message);
53+
diag.span_label(span, label);
54+
if let Some(note) = note {
55+
diag.note(note);
56+
}
57+
diag.emit();
58+
ErrorReported
59+
}
60+
61+
impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
62+
pub fn parse(tcx: TyCtxt<'a, 'gcx, 'tcx>,
63+
trait_def_id: DefId,
64+
items: &[NestedMetaItem],
65+
span: Span,
66+
is_root: bool)
67+
-> Result<Self, ErrorReported>
68+
{
69+
let mut errored = false;
70+
let mut item_iter = items.iter();
71+
72+
let condition = if is_root {
73+
None
74+
} else {
75+
let cond = item_iter.next().ok_or_else(|| {
76+
parse_error(tcx, span,
77+
"empty `on`-clause in `#[rustc_on_unimplemented]`",
78+
"empty on-clause here",
79+
None)
80+
})?.meta_item().ok_or_else(|| {
81+
parse_error(tcx, span,
82+
"invalid `on`-clause in `#[rustc_on_unimplemented]`",
83+
"invalid on-clause here",
84+
None)
85+
})?;
86+
attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
87+
Some(cond.clone())
88+
};
89+
90+
let mut message = None;
91+
let mut label = None;
92+
let mut subcommands = vec![];
93+
for item in item_iter {
94+
if item.check_name("message") && message.is_none() {
95+
if let Some(message_) = item.value_str() {
96+
message = Some(OnUnimplementedFormatString::try_parse(
97+
tcx, trait_def_id, message_.as_str(), span)?);
98+
continue;
99+
}
100+
} else if item.check_name("label") && label.is_none() {
101+
if let Some(label_) = item.value_str() {
102+
label = Some(OnUnimplementedFormatString::try_parse(
103+
tcx, trait_def_id, label_.as_str(), span)?);
104+
continue;
105+
}
106+
} else if item.check_name("on") && is_root &&
107+
message.is_none() && label.is_none()
108+
{
109+
if let Some(items) = item.meta_item_list() {
110+
if let Ok(subcommand) =
111+
Self::parse(tcx, trait_def_id, &items, item.span, false)
112+
{
113+
subcommands.push(subcommand);
114+
} else {
115+
errored = true;
116+
}
117+
continue
118+
}
119+
}
120+
121+
// nothing found
122+
parse_error(tcx, item.span,
123+
"this attribute must have a valid value",
124+
"expected value here",
125+
Some(r#"eg `#[rustc_on_unimplemented = "foo"]`"#));
126+
}
127+
128+
if errored {
129+
Err(ErrorReported)
130+
} else {
131+
Ok(OnUnimplementedDirective { condition, message, label, subcommands })
132+
}
133+
}
134+
135+
27136
pub fn of_item(tcx: TyCtxt<'a, 'gcx, 'tcx>,
28137
trait_def_id: DefId,
29-
impl_def_id: DefId,
30-
span: Span)
138+
impl_def_id: DefId)
31139
-> Result<Option<Self>, ErrorReported>
32140
{
33141
let attrs = tcx.get_attrs(impl_def_id);
@@ -40,20 +148,52 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedInfo {
40148
return Ok(None);
41149
};
42150

43-
let span = attr.span.substitute_dummy(span);
44-
if let Some(label) = attr.value_str() {
45-
Ok(Some(OnUnimplementedInfo {
46-
label: OnUnimplementedFormatString::try_parse(
47-
tcx, trait_def_id, label.as_str(), span)?
151+
let result = if let Some(items) = attr.meta_item_list() {
152+
Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
153+
} else if let Some(value) = attr.value_str() {
154+
Ok(Some(OnUnimplementedDirective {
155+
condition: None,
156+
message: None,
157+
subcommands: vec![],
158+
label: Some(OnUnimplementedFormatString::try_parse(
159+
tcx, trait_def_id, value.as_str(), attr.span)?)
48160
}))
49161
} else {
50-
struct_span_err!(
51-
tcx.sess, span, E0232,
52-
"this attribute must have a value")
53-
.span_label(attr.span, "attribute requires a value")
54-
.note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`"))
55-
.emit();
56-
Err(ErrorReported)
162+
return Err(parse_error(tcx, attr.span,
163+
"`#[rustc_on_unimplemented]` requires a value",
164+
"value required here",
165+
Some(r#"eg `#[rustc_on_unimplemented = "foo"]`"#)));
166+
};
167+
debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
168+
result
169+
}
170+
171+
pub fn evaluate(&self,
172+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
173+
trait_ref: ty::TraitRef<'tcx>,
174+
options: &[&str])
175+
-> OnUnimplementedNote
176+
{
177+
let mut message = None;
178+
let mut label = None;
179+
180+
for command in self.subcommands.iter().chain(Some(self)).rev() {
181+
if let Some(ref condition) = command.condition {
182+
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
183+
options.iter().any(|o| c.check_name(o))
184+
}) {
185+
debug!("evaluate: skipping {:?} due to condition", command);
186+
continue
187+
}
188+
}
189+
debug!("evaluate: {:?} succeeded", command);
190+
message = command.message.clone();
191+
label = command.label.clone();
192+
}
193+
194+
OnUnimplementedNote {
195+
label: label.map(|l| l.format(tcx, trait_ref)),
196+
message: message.map(|m| m.format(tcx, trait_ref))
57197
}
58198
}
59199
}

src/librustc_typeck/check/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,8 +1218,8 @@ fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12181218
item: &hir::Item) {
12191219
let item_def_id = tcx.hir.local_def_id(item.id);
12201220
// an error would be reported if this fails.
1221-
let _ = traits::OnUnimplementedInfo::of_item(
1222-
tcx, trait_def_id, item_def_id, item.span);
1221+
let _ = traits::OnUnimplementedDirective::of_item(
1222+
tcx, trait_def_id, item_def_id);
12231223
}
12241224

12251225
fn report_forbidden_specialization<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

src/libsyntax/attr.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,20 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
585585

586586
/// Tests if a cfg-pattern matches the cfg set
587587
pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
588+
eval_condition(cfg, sess, &mut |cfg| {
589+
if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
590+
gated_cfg.check_and_emit(sess, feats);
591+
}
592+
sess.config.contains(&(cfg.name(), cfg.value_str()))
593+
})
594+
}
595+
596+
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
597+
/// evaluate individual items.
598+
pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
599+
-> bool
600+
where F: FnMut(&ast::MetaItem) -> bool
601+
{
588602
match cfg.node {
589603
ast::MetaItemKind::List(ref mis) => {
590604
for mi in mis.iter() {
@@ -598,18 +612,18 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
598612
// that they won't fail with the loop above.
599613
match &*cfg.name.as_str() {
600614
"any" => mis.iter().any(|mi| {
601-
cfg_matches(mi.meta_item().unwrap(), sess, features)
615+
eval_condition(mi.meta_item().unwrap(), sess, eval)
602616
}),
603617
"all" => mis.iter().all(|mi| {
604-
cfg_matches(mi.meta_item().unwrap(), sess, features)
618+
eval_condition(mi.meta_item().unwrap(), sess, eval)
605619
}),
606620
"not" => {
607621
if mis.len() != 1 {
608622
span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
609623
return false;
610624
}
611625

612-
!cfg_matches(mis[0].meta_item().unwrap(), sess, features)
626+
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
613627
},
614628
p => {
615629
span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
@@ -618,10 +632,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
618632
}
619633
},
620634
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
621-
if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
622-
gated_cfg.check_and_emit(sess, feats);
623-
}
624-
sess.config.contains(&(cfg.name(), cfg.value_str()))
635+
eval(cfg)
625636
}
626637
}
627638
}

src/test/compile-fail/E0232.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#[rustc_on_unimplemented]
1414
//~^ ERROR E0232
15-
//~| NOTE attribute requires a value
15+
//~| NOTE value required here
1616
//~| NOTE eg `#[rustc_on_unimplemented = "foo"]`
1717
trait Bar {}
1818

0 commit comments

Comments
 (0)