diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2015-10-13 06:01:31 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2015-10-13 06:01:31 +0300 |
| commit | ab671552c3b44906b63c07f2676492b766f2a7c3 (patch) | |
| tree | 5fcfa78f184afcc1bd1cf0cbf5be83f25ec720b9 /src/libsyntax/attr.rs | |
| parent | 843e528fd0c09666cc5f2896258f88c25eaacb29 (diff) | |
| download | rust-ab671552c3b44906b63c07f2676492b766f2a7c3.tar.gz rust-ab671552c3b44906b63c07f2676492b766f2a7c3.zip | |
Refactor attr::Stability
Stricter checking + enforcement of invariants at compile time
Diffstat (limited to 'src/libsyntax/attr.rs')
| -rw-r--r-- | src/libsyntax/attr.rs | 294 |
1 files changed, 171 insertions, 123 deletions
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index bd99d33222d..eeb832d48b0 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -27,7 +27,6 @@ use ptr::P; use std::cell::{RefCell, Cell}; use std::collections::HashSet; -use std::fmt; thread_local! { static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new()) @@ -382,174 +381,223 @@ pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::Me } } -/// Represents the #[deprecated] and friends attributes. +/// Represents the #[stable], #[unstable] and #[deprecated] attributes. #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)] pub struct Stability { pub level: StabilityLevel, pub feature: InternedString, - pub since: Option<InternedString>, - pub deprecated_since: Option<InternedString>, - // The reason for the current stability level. If deprecated, the - // reason for deprecation. - pub reason: Option<InternedString>, - // The relevant rust-lang issue - pub issue: Option<u32> + pub depr: Option<Deprecation>, } /// The available stability levels. -#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)] +#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] pub enum StabilityLevel { - Unstable, - Stable, + // Reason for the current stability level and the relevant rust-lang issue + Unstable { reason: Option<InternedString>, issue: u32 }, + Stable { since: InternedString }, } -impl fmt::Display for StabilityLevel { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } +#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] +pub struct Deprecation { + pub since: InternedString, + pub reason: InternedString, } -fn find_stability_generic<'a, - AM: AttrMetaMethods, - I: Iterator<Item=&'a AM>> - (diagnostic: &SpanHandler, attrs: I, item_sp: Span) - -> (Option<Stability>, Vec<&'a AM>) { +impl StabilityLevel { + pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }} + pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }} +} +fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, + attrs_iter: I, + item_sp: Span) + -> Option<Stability> + where I: Iterator<Item = &'a Attribute> +{ let mut stab: Option<Stability> = None; - let mut deprecated: Option<(Option<InternedString>, Option<InternedString>)> = None; - let mut used_attrs: Vec<&'a AM> = vec![]; + let mut depr: Option<Deprecation> = None; - 'outer: for attr in attrs { + 'outer: for attr in attrs_iter { let tag = attr.name(); - let tag = &tag[..]; + let tag = &*tag; if tag != "deprecated" && tag != "unstable" && tag != "stable" { continue // not a stability level } - used_attrs.push(attr); - - let (feature, since, reason, issue) = match attr.meta_item_list() { - Some(metas) => { - let mut feature = None; - let mut since = None; - let mut reason = None; - let mut issue = None; - for meta in metas { - match &*meta.name() { - "feature" => { - match meta.value_str() { - Some(v) => feature = Some(v), - None => { - diagnostic.span_err(meta.span, "incorrect meta item"); - continue 'outer; - } + mark_used(attr); + + if let Some(metas) = attr.meta_item_list() { + let get = |meta: &MetaItem, item: &mut Option<InternedString>| { + if item.is_some() { + diagnostic.span_err(meta.span, &format!("multiple '{}' items", + meta.name())); + return false + } + if let Some(v) = meta.value_str() { + *item = Some(v); + true + } else { + diagnostic.span_err(meta.span, "incorrect meta item"); + false + } + }; + + match tag { + "deprecated" => { + if depr.is_some() { + diagnostic.span_err(item_sp, "multiple deprecated attributes"); + break + } + + let mut since = None; + let mut reason = None; + for meta in metas { + match &*meta.name() { + "since" => if !get(meta, &mut since) { continue 'outer }, + "reason" => if !get(meta, &mut reason) { continue 'outer }, + _ => { + diagnostic.span_err(meta.span, &format!("unknown meta item '{}'", + meta.name())); + continue 'outer } } - "since" => { - match meta.value_str() { - Some(v) => since = Some(v), - None => { - diagnostic.span_err(meta.span, "incorrect meta item"); - continue 'outer; - } - } + } + + match (since, reason) { + (Some(since), Some(reason)) => { + depr = Some(Deprecation { + since: since, + reason: reason, + }) } - "reason" => { - match meta.value_str() { - Some(v) => reason = Some(v), - None => { - diagnostic.span_err(meta.span, "incorrect meta item"); - continue 'outer; - } - } + (None, _) => { + diagnostic.span_err(attr.span(), "missing 'since'"); + continue } - "issue" => { - match meta.value_str().and_then(|s| s.parse().ok()) { - Some(v) => issue = Some(v), - None => { - diagnostic.span_err(meta.span, "incorrect meta item"); - continue 'outer; - } - } + _ => { + diagnostic.span_err(attr.span(), "missing 'reason'"); + continue } - _ => {} } } - (feature, since, reason, issue) - } - None => { - diagnostic.span_err(attr.span(), "incorrect stability attribute type"); - continue - } - }; + "unstable" => { + if stab.is_some() { + diagnostic.span_err(item_sp, "multiple stability levels"); + break + } - // Deprecated tags don't require feature names - if feature == None && tag != "deprecated" { - diagnostic.span_err(attr.span(), "missing 'feature'"); - } + let mut feature = None; + let mut reason = None; + let mut issue = None; + for meta in metas { + match &*meta.name() { + "feature" => if !get(meta, &mut feature) { continue 'outer }, + "reason" => if !get(meta, &mut reason) { continue 'outer }, + "issue" => if !get(meta, &mut issue) { continue 'outer }, + _ => { + diagnostic.span_err(meta.span, &format!("unknown meta item '{}'", + meta.name())); + continue 'outer + } + } + } - // Unstable tags don't require a version - if since == None && tag != "unstable" { - diagnostic.span_err(attr.span(), "missing 'since'"); - } + match (feature, reason, issue) { + (Some(feature), reason, Some(issue)) => { + stab = Some(Stability { + level: Unstable { + reason: reason, + issue: { + if let Ok(issue) = issue.parse() { + issue + } else { + diagnostic.span_err(attr.span(), "incorrect 'issue'"); + continue + } + } + }, + feature: feature, + depr: None, + }) + } + (None, _, _) => { + diagnostic.span_err(attr.span(), "missing 'feature'"); + continue + } + _ => { + diagnostic.span_err(attr.span(), "missing 'issue'"); + continue + } + } + } + "stable" => { + if stab.is_some() { + diagnostic.span_err(item_sp, "multiple stability levels"); + break + } - if tag == "unstable" || tag == "stable" { - if stab.is_some() { - diagnostic.span_err(item_sp, "multiple stability levels"); - } + let mut feature = None; + let mut since = None; + for meta in metas { + match &*meta.name() { + "feature" => if !get(meta, &mut feature) { continue 'outer }, + "since" => if !get(meta, &mut since) { continue 'outer }, + _ => { + diagnostic.span_err(meta.span, &format!("unknown meta item '{}'", + meta.name())); + continue 'outer + } + } + } - let level = match tag { - "unstable" => Unstable, - "stable" => Stable, + match (feature, since) { + (Some(feature), Some(since)) => { + stab = Some(Stability { + level: Stable { + since: since, + }, + feature: feature, + depr: None, + }) + } + (None, _) => { + diagnostic.span_err(attr.span(), "missing 'feature'"); + continue + } + _ => { + diagnostic.span_err(attr.span(), "missing 'since'"); + continue + } + } + } _ => unreachable!() - }; - - stab = Some(Stability { - level: level, - feature: feature.unwrap_or(intern_and_get_ident("bogus")), - since: since, - deprecated_since: None, - reason: reason, - issue: issue, - }); - } else { // "deprecated" - if deprecated.is_some() { - diagnostic.span_err(item_sp, "multiple deprecated attributes"); } - - deprecated = Some((since, reason)); + } else { + diagnostic.span_err(attr.span(), "incorrect stability attribute type"); + continue } } // Merge the deprecation info into the stability info - if deprecated.is_some() { - match stab { - Some(ref mut s) => { - let (since, reason) = deprecated.unwrap(); - s.deprecated_since = since; - s.reason = reason; - } - None => { - diagnostic.span_err(item_sp, "deprecated attribute must be paired with \ - either stable or unstable attribute"); + if let Some(depr) = depr { + if let Some(ref mut stab) = stab { + if let Unstable {reason: ref mut reason @ None, ..} = stab.level { + *reason = Some(depr.reason.clone()) } + stab.depr = Some(depr); + } else { + diagnostic.span_err(item_sp, "deprecated attribute must be paired with \ + either stable or unstable attribute"); } - } else if stab.as_ref().map_or(false, |s| s.level == Unstable && s.issue.is_none()) { - // non-deprecated unstable items need to point to issues. - diagnostic.span_err(item_sp, - "non-deprecated unstable items need to point \ - to an issue with `issue = \"NNN\"`"); } - (stab, used_attrs) + stab } /// Find the first stability attribute. `None` if none exists. pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute], item_sp: Span) -> Option<Stability> { - let (s, used) = find_stability_generic(diagnostic, attrs.iter(), item_sp); - for used in used { mark_used(used) } - return s; + find_stability_generic(diagnostic, attrs.iter(), item_sp) } pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) { |
