]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/middle/stability.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / middle / stability.rs
CommitLineData
1a4d82fc
JJ
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
b039eaaf
SL
4pub use self::StabilityLevel::*;
5
9fa01778 6use crate::ty::{self, TyCtxt};
fc512014 7use rustc_ast::NodeId;
3dfed10e 8use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
dfeec247
XL
9use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10use rustc_errors::{Applicability, DiagnosticBuilder};
11use rustc_feature::GateIssue;
12use rustc_hir as hir;
13use rustc_hir::def::DefKind;
14use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
15use rustc_hir::{self, HirId};
1b1a35ee 16use rustc_middle::ty::print::with_no_trimmed_paths;
74b04a01
XL
17use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
18use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer};
19use rustc_session::parse::feature_err_issue;
ba9703b0 20use rustc_session::{DiagnosticMessageId, Session};
dfeec247
XL
21use rustc_span::symbol::{sym, Symbol};
22use rustc_span::{MultiSpan, Span};
1a4d82fc 23
60c5eb7d
XL
24use std::num::NonZeroU32;
25
e74abb32 26#[derive(PartialEq, Clone, Copy, Debug)]
b039eaaf
SL
27pub enum StabilityLevel {
28 Unstable,
29 Stable,
30}
31
32impl StabilityLevel {
33 pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
34 if level.is_stable() { Stable } else { Unstable }
35 }
36}
37
5bcae85e 38/// An entry in the `depr_map`.
60c5eb7d 39#[derive(Clone, HashStable)]
5bcae85e
SL
40pub struct DeprecationEntry {
41 /// The metadata of the attribute associated with this entry.
42 pub attr: Deprecation,
9fa01778 43 /// The `DefId` where the attr was originally attached. `None` for non-local
5bcae85e 44 /// `DefId`'s.
ea8adc8c 45 origin: Option<HirId>,
5bcae85e
SL
46}
47
48impl DeprecationEntry {
dfeec247
XL
49 pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
50 DeprecationEntry { attr, origin: Some(id) }
5bcae85e
SL
51 }
52
ea8adc8c 53 pub fn external(attr: Deprecation) -> DeprecationEntry {
dfeec247 54 DeprecationEntry { attr, origin: None }
5bcae85e
SL
55 }
56
57 pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
58 match (self.origin, other.origin) {
59 (Some(o1), Some(o2)) => o1 == o2,
dfeec247 60 _ => false,
5bcae85e
SL
61 }
62 }
63}
64
1a4d82fc 65/// A stability index, giving the stability level for items and methods.
60c5eb7d 66#[derive(HashStable)]
62682a34
SL
67pub struct Index<'tcx> {
68 /// This is mostly a cache, except the stabilities of local items
69 /// are filled by the annotator.
dfeec247
XL
70 pub stab_map: FxHashMap<HirId, &'tcx Stability>,
71 pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
72 pub depr_map: FxHashMap<HirId, DeprecationEntry>,
62682a34
SL
73
74 /// Maps for each crate whether it is part of the staged API.
dfeec247 75 pub staged_api: FxHashMap<CrateNum, bool>,
476ff2be
SL
76
77 /// Features enabled for this crate.
dfeec247 78 pub active_features: FxHashSet<Symbol>,
476ff2be
SL
79}
80
dc9dc135 81impl<'tcx> Index<'tcx> {
ea8adc8c
XL
82 pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
83 self.stab_map.get(&id).cloned()
85aaf69f
SL
84 }
85
60c5eb7d
XL
86 pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
87 self.const_stab_map.get(&id).cloned()
88 }
89
ea8adc8c
XL
90 pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
91 self.depr_map.get(&id).cloned()
85aaf69f
SL
92 }
93}
94
416331ca 95pub fn report_unstable(
e74abb32
XL
96 sess: &Session,
97 feature: Symbol,
98 reason: Option<Symbol>,
60c5eb7d 99 issue: Option<NonZeroU32>,
e74abb32
XL
100 is_soft: bool,
101 span: Span,
74b04a01 102 soft_handler: impl FnOnce(&'static Lint, Span, &str),
416331ca
XL
103) {
104 let msg = match reason {
105 Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
dfeec247 106 None => format!("use of unstable library feature '{}'", &feature),
416331ca
XL
107 };
108
109 let msp: MultiSpan = span.into();
74b04a01 110 let sm = &sess.parse_sess.source_map();
dfeec247 111 let span_key = msp.primary_span().and_then(|sp: Span| {
416331ca 112 if !sp.is_dummy() {
74b04a01 113 let file = sm.lookup_char_pos(sp.lo()).file;
ba9703b0 114 if file.is_imported() { None } else { Some(span) }
416331ca
XL
115 } else {
116 None
117 }
dfeec247 118 });
416331ca
XL
119
120 let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
121 let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
122 if fresh {
123 if is_soft {
74b04a01 124 soft_handler(SOFT_UNSTABLE, span, &msg)
416331ca 125 } else {
60c5eb7d
XL
126 feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
127 .emit();
416331ca
XL
128 }
129 }
130}
131
9fa01778 132/// Checks whether an item marked with `deprecated(since="X")` is currently
0731742a 133/// deprecated (i.e., whether X is not greater than the current rustc version).
3dfed10e 134pub fn deprecation_in_effect(is_since_rustc_version: bool, since: Option<&str>) -> bool {
83c7162d 135 fn parse_version(ver: &str) -> Vec<u32> {
0731742a 136 // We ignore non-integer components of the version (e.g., "nightly").
83c7162d
XL
137 ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
138 }
139
fc512014
XL
140 if !is_since_rustc_version {
141 // The `since` field doesn't have semantic purpose in the stable `deprecated`
142 // attribute, only in `rustc_deprecated`.
143 return true;
83c7162d 144 }
fc512014
XL
145
146 if let Some(since) = since {
147 if since == "TBD" {
148 return false;
149 }
150
151 if let Some(rustc) = option_env!("CFG_RELEASE") {
152 let since: Vec<u32> = parse_version(&since);
153 let rustc: Vec<u32> = parse_version(rustc);
154 // We simply treat invalid `since` attributes as relating to a previous
155 // Rust version, thus always displaying the warning.
156 if since.len() != 3 {
157 return true;
158 }
159 return since <= rustc;
160 }
161 };
162
163 // Assume deprecation is in effect if "since" field is missing
164 // or if we can't determine the current Rust version.
165 true
83c7162d
XL
166}
167
416331ca 168pub fn deprecation_suggestion(
dfeec247 169 diag: &mut DiagnosticBuilder<'_>,
3dfed10e 170 kind: &str,
dfeec247
XL
171 suggestion: Option<Symbol>,
172 span: Span,
416331ca
XL
173) {
174 if let Some(suggestion) = suggestion {
175 diag.span_suggestion(
176 span,
3dfed10e 177 &format!("replace the use of the deprecated {}", kind),
416331ca
XL
178 suggestion.to_string(),
179 Applicability::MachineApplicable,
180 );
181 }
182}
183
3dfed10e 184pub fn deprecation_message(depr: &Deprecation, kind: &str, path: &str) -> (String, &'static Lint) {
fc512014
XL
185 let since = depr.since.map(Symbol::as_str);
186 let (message, lint) = if deprecation_in_effect(depr.is_since_rustc_version, since.as_deref()) {
3dfed10e 187 (format!("use of deprecated {} `{}`", kind, path), DEPRECATED)
416331ca 188 } else {
dfeec247 189 (
fc512014
XL
190 if since.as_deref() == Some("TBD") {
191 format!(
192 "use of {} `{}` that will be deprecated in a future Rust version",
193 kind, path
194 )
195 } else {
196 format!(
197 "use of {} `{}` that will be deprecated in future version {}",
198 kind,
199 path,
200 since.unwrap()
201 )
202 },
74b04a01 203 DEPRECATED_IN_FUTURE,
dfeec247 204 )
416331ca 205 };
3dfed10e
XL
206 let message = match depr.note {
207 Some(reason) => format!("{}: {}", message, reason),
208 None => message,
209 };
210 (message, lint)
416331ca
XL
211}
212
213pub fn early_report_deprecation(
dfeec247 214 lint_buffer: &'a mut LintBuffer,
416331ca
XL
215 message: &str,
216 suggestion: Option<Symbol>,
217 lint: &'static Lint,
218 span: Span,
fc512014 219 node_id: NodeId,
416331ca 220) {
dfeec247 221 if span.in_derive_expansion() {
416331ca
XL
222 return;
223 }
224
225 let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
fc512014 226 lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag);
416331ca
XL
227}
228
229fn late_report_deprecation(
230 tcx: TyCtxt<'_>,
231 message: &str,
232 suggestion: Option<Symbol>,
233 lint: &'static Lint,
234 span: Span,
416331ca 235 hir_id: HirId,
3dfed10e 236 def_id: DefId,
416331ca 237) {
dfeec247 238 if span.in_derive_expansion() {
416331ca
XL
239 return;
240 }
241
74b04a01
XL
242 tcx.struct_span_lint_hir(lint, hir_id, span, |lint| {
243 let mut diag = lint.build(message);
244 if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
3dfed10e
XL
245 let kind = tcx.def_kind(def_id).descr(def_id);
246 deprecation_suggestion(&mut diag, kind, suggestion, span);
74b04a01
XL
247 }
248 diag.emit()
249 });
416331ca
XL
250}
251
0531ce1d
XL
252/// Result of `TyCtxt::eval_stability`.
253pub enum EvalResult {
254 /// We can use the item because it is stable or we provided the
255 /// corresponding feature gate.
256 Allow,
257 /// We cannot use the item because it is unstable and we did not provide the
258 /// corresponding feature gate.
dfeec247 259 Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
0531ce1d
XL
260 /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
261 Unmarked,
262}
263
dfeec247 264// See issue #38412.
29967ef6
XL
265fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
266 if tcx.def_kind(def_id) == DefKind::TyParam {
267 // Have no visibility, considered public for the purpose of this check.
268 return false;
dfeec247 269 }
29967ef6 270 match tcx.visibility(def_id) {
dfeec247
XL
271 // Must check stability for `pub` items.
272 ty::Visibility::Public => false,
476ff2be 273
dfeec247
XL
274 // These are not visible outside crate; therefore
275 // stability markers are irrelevant, if even present.
276 ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
476ff2be 277 }
dfeec247 278}
476ff2be 279
dfeec247 280impl<'tcx> TyCtxt<'tcx> {
0531ce1d
XL
281 /// Evaluates the stability of an item.
282 ///
283 /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
284 /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
285 /// unstable feature otherwise.
286 ///
287 /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
288 /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
289 /// `id`.
532ac7d7 290 pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
476ff2be 291 // Deprecated attributes apply in-crate and cross-crate.
0531ce1d
XL
292 if let Some(id) = id {
293 if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
dfeec247
XL
294 let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id));
295 let skip = self
f9f354fc 296 .lookup_deprecation_entry(parent_def_id.to_def_id())
dfeec247 297 .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
0731742a 298
3dfed10e
XL
299 // #[deprecated] doesn't emit a notice if we're not on the
300 // topmost deprecation. For example, if a struct is deprecated,
301 // the use of a field won't be linted.
302 //
303 // #[rustc_deprecated] however wants to emit down the whole
304 // hierarchy.
305 if !skip || depr_entry.attr.is_since_rustc_version {
1b1a35ee 306 let path = &with_no_trimmed_paths(|| self.def_path_str(def_id));
3dfed10e
XL
307 let kind = self.def_kind(def_id).descr(def_id);
308 let (message, lint) = deprecation_message(&depr_entry.attr, kind, path);
309 late_report_deprecation(
310 self,
311 &message,
312 depr_entry.attr.suggestion,
313 lint,
314 span,
315 id,
316 def_id,
317 );
0531ce1d 318 }
476ff2be 319 };
476ff2be
SL
320 }
321
dfeec247
XL
322 let is_staged_api =
323 self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some();
476ff2be 324 if !is_staged_api {
0531ce1d 325 return EvalResult::Allow;
9cc50fc6 326 }
476ff2be
SL
327
328 let stability = self.lookup_stability(def_id);
dfeec247
XL
329 debug!(
330 "stability: \
331 inspecting def_id={:?} span={:?} of stability={:?}",
332 def_id, span, stability
333 );
476ff2be 334
85aaf69f 335 // Only the cross-crate scenario matters when checking unstable APIs
476ff2be 336 let cross_crate = !def_id.is_local();
b039eaaf 337 if !cross_crate {
0531ce1d 338 return EvalResult::Allow;
b039eaaf
SL
339 }
340
0731742a 341 // Issue #38412: private items lack stability markers.
dfeec247 342 if skip_stability_check_due_to_privacy(self, def_id) {
0531ce1d 343 return EvalResult::Allow;
476ff2be 344 }
85aaf69f 345
476ff2be 346 match stability {
416331ca
XL
347 Some(&Stability {
348 level: attr::Unstable { reason, issue, is_soft }, feature, ..
349 }) => {
48663c56 350 if span.allows_unstable(feature) {
9fa01778
XL
351 debug!("stability: skipping span={:?} since it is internal", span);
352 return EvalResult::Allow;
353 }
0531ce1d
XL
354 if self.stability().active_features.contains(&feature) {
355 return EvalResult::Allow;
85aaf69f 356 }
7cac9316
XL
357
358 // When we're compiling the compiler itself we may pull in
359 // crates from crates.io, but those crates may depend on other
360 // crates also pulled in from crates.io. We want to ideally be
361 // able to compile everything without requiring upstream
362 // modifications, so in the case that this looks like a
0731742a 363 // `rustc_private` crate (e.g., a compiler crate) and we also have
7cac9316
XL
364 // the `-Z force-unstable-if-unmarked` flag present (we're
365 // compiling a compiler crate), then let this missing feature
366 // annotation slide.
60c5eb7d 367 if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
7cac9316 368 if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
0531ce1d 369 return EvalResult::Allow;
7cac9316
XL
370 }
371 }
372
416331ca 373 EvalResult::Deny { feature, reason, issue, is_soft }
0531ce1d
XL
374 }
375 Some(_) => {
376 // Stable APIs are always ok to call and deprecated APIs are
377 // handled by the lint emitting logic above.
378 EvalResult::Allow
379 }
dfeec247 380 None => EvalResult::Unmarked,
0531ce1d
XL
381 }
382 }
383
384 /// Checks if an item is stable or error out.
385 ///
386 /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
387 /// exist, emits an error.
388 ///
1b1a35ee
XL
389 /// This function will also check if the item is deprecated.
390 /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
532ac7d7 391 pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
1b1a35ee
XL
392 self.check_optional_stability(def_id, id, span, |span, def_id| {
393 // The API could be uncallable for other reasons, for example when a private module
394 // was referenced.
395 self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
396 })
397 }
398
399 /// Like `check_stability`, except that we permit items to have custom behaviour for
400 /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary
401 /// for default generic parameters, which only have stability attributes if they were
402 /// added after the type on which they're defined.
403 pub fn check_optional_stability(
404 self,
405 def_id: DefId,
406 id: Option<HirId>,
407 span: Span,
29967ef6 408 unmarked: impl FnOnce(Span, DefId),
1b1a35ee 409 ) {
74b04a01
XL
410 let soft_handler = |lint, span, msg: &_| {
411 self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
412 lint.build(msg).emit()
413 })
414 };
0531ce1d
XL
415 match self.eval_stability(def_id, id, span) {
416 EvalResult::Allow => {}
dfeec247
XL
417 EvalResult::Deny { feature, reason, issue, is_soft } => {
418 report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
419 }
1b1a35ee 420 EvalResult::Unmarked => unmarked(span, def_id),
85aaf69f
SL
421 }
422 }
85aaf69f 423
a7813a04 424 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
5bcae85e
SL
425 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
426 }
1a4d82fc 427}