lib_features.rs 5.6 KB
Newer Older
1
// Detecting lib features (i.e., features that are not lang features).
V
varkor 已提交
2
//
3
// These are declared using stability attributes (e.g., `#[stable (..)]`
V
varkor 已提交
4
// and `#[unstable (..)]`), but are not declared in one single location
V
varkor 已提交
5 6
// (unlike lang features), which means we need to collect them instead.

M
Mark Mansi 已提交
7
use crate::ty::TyCtxt;
T
Taiki Endo 已提交
8
use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
V
varkor 已提交
9 10
use syntax::symbol::Symbol;
use syntax::ast::{Attribute, MetaItem, MetaItemKind};
11
use syntax_pos::Span;
V
varkor 已提交
12
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
13
use rustc_macros::HashStable;
14
use errors::DiagnosticId;
V
varkor 已提交
15

16
#[derive(HashStable)]
V
varkor 已提交
17 18 19 20 21 22 23 24 25
pub struct LibFeatures {
    // A map from feature to stabilisation version.
    pub stable: FxHashMap<Symbol, Symbol>,
    pub unstable: FxHashSet<Symbol>,
}

impl LibFeatures {
    fn new() -> LibFeatures {
        LibFeatures {
26 27
            stable: Default::default(),
            unstable: Default::default(),
V
varkor 已提交
28 29 30
        }
    }

V
varkor 已提交
31 32
    pub fn to_vec(&self) -> Vec<(Symbol, Option<Symbol>)> {
        let mut all_features: Vec<_> = self.stable.iter().map(|(f, s)| (*f, Some(*s)))
V
varkor 已提交
33
            .chain(self.unstable.iter().map(|f| (*f, None)))
V
varkor 已提交
34 35 36
            .collect();
        all_features.sort_unstable_by_key(|f| f.0.as_str());
        all_features
V
varkor 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    }
}

pub struct LibFeatureCollector<'a, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    lib_features: LibFeatures,
}

impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
    fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LibFeatureCollector<'a, 'tcx> {
        LibFeatureCollector {
            tcx,
            lib_features: LibFeatures::new(),
        }
    }

53
    fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
V
varkor 已提交
54
        let stab_attrs = vec!["stable", "unstable", "rustc_const_unstable"];
55

56
        // Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
57 58 59 60 61 62 63 64 65 66 67
        // `#[rustc_const_unstable (..)]`).
        if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| {
            attr.check_name(stab_attr)
        }) {
            let meta_item = attr.meta();
            if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta_item {
                let mut feature = None;
                let mut since = None;
                for meta in metas {
                    if let Some(mi) = meta.meta_item() {
                        // Find the `feature = ".."` meta-item.
68 69 70
                        match (mi.name_or_empty().get(), mi.value_str()) {
                            ("feature", val) => feature = val,
                            ("since", val) => since = val,
71
                            _ => {}
V
varkor 已提交
72 73
                        }
                    }
74 75 76 77 78 79 80
                }
                if let Some(feature) = feature {
                    // This additional check for stability is to make sure we
                    // don't emit additional, irrelevant errors for malformed
                    // attributes.
                    if *stab_attr != "stable" || since.is_some() {
                        return Some((feature, since, attr.span));
V
varkor 已提交
81 82
                    }
                }
83 84 85 86
                // We need to iterate over the other attributes, because
                // `rustc_const_unstable` is not mutually exclusive with
                // the other stability attributes, so we can't just `break`
                // here.
V
varkor 已提交
87 88 89
            }
        }

90
        None
V
varkor 已提交
91 92 93 94 95 96 97 98
    }

    fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
        let already_in_stable = self.lib_features.stable.contains_key(&feature);
        let already_in_unstable = self.lib_features.unstable.contains(&feature);

        match (since, already_in_stable, already_in_unstable) {
            (Some(since), _, false) => {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
                if let Some(prev_since) = self.lib_features.stable.get(&feature) {
                    if *prev_since != since {
                        let msg = format!(
                            "feature `{}` is declared stable since {}, \
                             but was previously declared stable since {}",
                            feature,
                            since,
                            prev_since,
                        );
                        self.tcx.sess.struct_span_err_with_code(span, &msg,
                            DiagnosticId::Error("E0711".into())).emit();
                        return;
                    }
                }

V
varkor 已提交
114 115 116 117 118 119 120 121 122
                self.lib_features.stable.insert(feature, since);
            }
            (None, false, _) => {
                self.lib_features.unstable.insert(feature);
            }
            (Some(_), _, true) | (None, true, _) => {
                let msg = format!(
                    "feature `{}` is declared {}, but was previously declared {}",
                    feature,
123 124
                    if since.is_some() { "stable" } else { "unstable" },
                    if since.is_none() { "stable" } else { "unstable" },
V
varkor 已提交
125 126 127 128 129 130 131 132
                );
                self.tcx.sess.struct_span_err_with_code(span, &msg,
                    DiagnosticId::Error("E0711".into())).emit();
            }
        }
    }
}

133 134
impl<'a, 'tcx> Visitor<'tcx> for LibFeatureCollector<'a, 'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
135
        NestedVisitorMap::All(&self.tcx.hir())
V
varkor 已提交
136 137
    }

138 139 140 141
    fn visit_attribute(&mut self, attr: &'tcx Attribute) {
        if let Some((feature, stable, span)) = self.extract(attr) {
            self.collect_feature(feature, stable, span);
        }
V
varkor 已提交
142 143 144 145 146
    }
}

pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LibFeatures {
    let mut collector = LibFeatureCollector::new(tcx);
147
    intravisit::walk_crate(&mut collector, tcx.hir().krate());
V
varkor 已提交
148 149
    collector.lib_features
}