about summary refs log tree commit diff
path: root/src/libsyntax/attr.rs
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2015-10-13 06:01:31 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2015-10-13 06:01:31 +0300
commitab671552c3b44906b63c07f2676492b766f2a7c3 (patch)
tree5fcfa78f184afcc1bd1cf0cbf5be83f25ec720b9 /src/libsyntax/attr.rs
parent843e528fd0c09666cc5f2896258f88c25eaacb29 (diff)
downloadrust-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.rs294
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>]) {