|
| 1 | +//! A pass that annotates every item and method with its stability level, |
| 2 | +//! propagating default levels lexically from parent to children ast nodes. |
| 3 | +
|
| 4 | +pub use self::StabilityLevel::*; |
| 5 | + |
| 6 | +use rustc_ast::CRATE_NODE_ID; |
| 7 | +use rustc_attr::{self as attr, ConstStability, Deprecation, Stability}; |
| 8 | +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
| 9 | +use rustc_errors::{Applicability, DiagnosticBuilder}; |
| 10 | +use rustc_feature::GateIssue; |
| 11 | +use rustc_hir::def_id::CrateNum; |
| 12 | +use rustc_hir::{self, HirId}; |
| 13 | +use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; |
| 14 | +use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer}; |
| 15 | +use rustc_session::parse::feature_err_issue; |
| 16 | +use rustc_session::{DiagnosticMessageId, Session}; |
| 17 | +use rustc_span::symbol::Symbol; |
| 18 | +use rustc_span::{MultiSpan, Span}; |
| 19 | + |
| 20 | +use std::num::NonZeroU32; |
| 21 | + |
| 22 | +#[derive(PartialEq, Clone, Copy, Debug)] |
| 23 | +pub enum StabilityLevel { |
| 24 | + Unstable, |
| 25 | + Stable, |
| 26 | +} |
| 27 | + |
| 28 | +impl StabilityLevel { |
| 29 | + pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { |
| 30 | + if level.is_stable() { Stable } else { Unstable } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +/// An entry in the `depr_map`. |
| 35 | +#[derive(Clone, HashStable_Generic)] |
| 36 | +pub struct DeprecationEntry { |
| 37 | + /// The metadata of the attribute associated with this entry. |
| 38 | + pub attr: Deprecation, |
| 39 | + /// The `DefId` where the attr was originally attached. `None` for non-local |
| 40 | + /// `DefId`'s. |
| 41 | + origin: Option<HirId>, |
| 42 | +} |
| 43 | + |
| 44 | +impl DeprecationEntry { |
| 45 | + pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry { |
| 46 | + DeprecationEntry { attr, origin: Some(id) } |
| 47 | + } |
| 48 | + |
| 49 | + pub fn external(attr: Deprecation) -> DeprecationEntry { |
| 50 | + DeprecationEntry { attr, origin: None } |
| 51 | + } |
| 52 | + |
| 53 | + pub fn same_origin(&self, other: &DeprecationEntry) -> bool { |
| 54 | + match (self.origin, other.origin) { |
| 55 | + (Some(o1), Some(o2)) => o1 == o2, |
| 56 | + _ => false, |
| 57 | + } |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +/// A stability index, giving the stability level for items and methods. |
| 62 | +#[derive(HashStable_Generic)] |
| 63 | +pub struct Index<'tcx> { |
| 64 | + /// This is mostly a cache, except the stabilities of local items |
| 65 | + /// are filled by the annotator. |
| 66 | + pub stab_map: FxHashMap<HirId, &'tcx Stability>, |
| 67 | + pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>, |
| 68 | + pub depr_map: FxHashMap<HirId, DeprecationEntry>, |
| 69 | + |
| 70 | + /// Maps for each crate whether it is part of the staged API. |
| 71 | + pub staged_api: FxHashMap<CrateNum, bool>, |
| 72 | + |
| 73 | + /// Features enabled for this crate. |
| 74 | + pub active_features: FxHashSet<Symbol>, |
| 75 | +} |
| 76 | + |
| 77 | +impl<'tcx> Index<'tcx> { |
| 78 | + pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> { |
| 79 | + self.stab_map.get(&id).cloned() |
| 80 | + } |
| 81 | + |
| 82 | + pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> { |
| 83 | + self.const_stab_map.get(&id).cloned() |
| 84 | + } |
| 85 | + |
| 86 | + pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> { |
| 87 | + self.depr_map.get(&id).cloned() |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +pub fn report_unstable( |
| 92 | + sess: &Session, |
| 93 | + feature: Symbol, |
| 94 | + reason: Option<Symbol>, |
| 95 | + issue: Option<NonZeroU32>, |
| 96 | + is_soft: bool, |
| 97 | + span: Span, |
| 98 | + soft_handler: impl FnOnce(&'static Lint, Span, &str), |
| 99 | +) { |
| 100 | + let msg = match reason { |
| 101 | + Some(r) => format!("use of unstable library feature '{}': {}", feature, r), |
| 102 | + None => format!("use of unstable library feature '{}'", &feature), |
| 103 | + }; |
| 104 | + |
| 105 | + let msp: MultiSpan = span.into(); |
| 106 | + let sm = &sess.parse_sess.source_map(); |
| 107 | + let span_key = msp.primary_span().and_then(|sp: Span| { |
| 108 | + if !sp.is_dummy() { |
| 109 | + let file = sm.lookup_char_pos(sp.lo()).file; |
| 110 | + if file.is_imported() { None } else { Some(span) } |
| 111 | + } else { |
| 112 | + None |
| 113 | + } |
| 114 | + }); |
| 115 | + |
| 116 | + let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); |
| 117 | + let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); |
| 118 | + if fresh { |
| 119 | + if is_soft { |
| 120 | + soft_handler(SOFT_UNSTABLE, span, &msg) |
| 121 | + } else { |
| 122 | + feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg) |
| 123 | + .emit(); |
| 124 | + } |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +/// Checks whether an item marked with `deprecated(since="X")` is currently |
| 129 | +/// deprecated (i.e., whether X is not greater than the current rustc version). |
| 130 | +pub fn deprecation_in_effect(is_since_rustc_version: bool, since: Option<&str>) -> bool { |
| 131 | + let since = if let Some(since) = since { |
| 132 | + if is_since_rustc_version { |
| 133 | + since |
| 134 | + } else { |
| 135 | + // We assume that the deprecation is in effect if it's not a |
| 136 | + // rustc version. |
| 137 | + return true; |
| 138 | + } |
| 139 | + } else { |
| 140 | + // If since attribute is not set, then we're definitely in effect. |
| 141 | + return true; |
| 142 | + }; |
| 143 | + fn parse_version(ver: &str) -> Vec<u32> { |
| 144 | + // We ignore non-integer components of the version (e.g., "nightly"). |
| 145 | + ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() |
| 146 | + } |
| 147 | + |
| 148 | + if let Some(rustc) = option_env!("CFG_RELEASE") { |
| 149 | + let since: Vec<u32> = parse_version(&since); |
| 150 | + let rustc: Vec<u32> = parse_version(rustc); |
| 151 | + // We simply treat invalid `since` attributes as relating to a previous |
| 152 | + // Rust version, thus always displaying the warning. |
| 153 | + if since.len() != 3 { |
| 154 | + return true; |
| 155 | + } |
| 156 | + since <= rustc |
| 157 | + } else { |
| 158 | + // By default, a deprecation warning applies to |
| 159 | + // the current version of the compiler. |
| 160 | + true |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +pub fn deprecation_suggestion( |
| 165 | + diag: &mut DiagnosticBuilder<'_>, |
| 166 | + kind: &str, |
| 167 | + suggestion: Option<Symbol>, |
| 168 | + span: Span, |
| 169 | +) { |
| 170 | + if let Some(suggestion) = suggestion { |
| 171 | + diag.span_suggestion( |
| 172 | + span, |
| 173 | + &format!("replace the use of the deprecated {}", kind), |
| 174 | + suggestion.to_string(), |
| 175 | + Applicability::MachineApplicable, |
| 176 | + ); |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +pub fn deprecation_message(depr: &Deprecation, kind: &str, path: &str) -> (String, &'static Lint) { |
| 181 | + let (message, lint) = if deprecation_in_effect( |
| 182 | + depr.is_since_rustc_version, |
| 183 | + depr.since.map(Symbol::as_str).as_deref(), |
| 184 | + ) { |
| 185 | + (format!("use of deprecated {} `{}`", kind, path), DEPRECATED) |
| 186 | + } else { |
| 187 | + ( |
| 188 | + format!( |
| 189 | + "use of {} `{}` that will be deprecated in future version {}", |
| 190 | + kind, |
| 191 | + path, |
| 192 | + depr.since.unwrap() |
| 193 | + ), |
| 194 | + DEPRECATED_IN_FUTURE, |
| 195 | + ) |
| 196 | + }; |
| 197 | + let message = match depr.note { |
| 198 | + Some(reason) => format!("{}: {}", message, reason), |
| 199 | + None => message, |
| 200 | + }; |
| 201 | + (message, lint) |
| 202 | +} |
| 203 | + |
| 204 | +pub fn early_report_deprecation<'a>( |
| 205 | + lint_buffer: &'a mut LintBuffer, |
| 206 | + message: &str, |
| 207 | + suggestion: Option<Symbol>, |
| 208 | + lint: &'static Lint, |
| 209 | + span: Span, |
| 210 | +) { |
| 211 | + if span.in_derive_expansion() { |
| 212 | + return; |
| 213 | + } |
| 214 | + |
| 215 | + let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); |
| 216 | + lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); |
| 217 | +} |
0 commit comments