]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/middle/stability.rs
New upstream version 1.63.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
3c0e092e 6use crate::ty::{self, DefIdTree, TyCtxt};
fc512014 7use rustc_ast::NodeId;
3dfed10e 8use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
5e7ed085
FG
9use rustc_data_structures::fx::FxHashMap;
10use rustc_errors::{Applicability, Diagnostic};
dfeec247 11use rustc_feature::GateIssue;
dfeec247 12use rustc_hir::def::DefKind;
04454e1e 13use rustc_hir::def_id::{DefId, LocalDefId};
923072b8 14use rustc_hir::{self as hir, HirId};
1b1a35ee 15use rustc_middle::ty::print::with_no_trimmed_paths;
74b04a01 16use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
c295e0f8 17use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
74b04a01 18use rustc_session::parse::feature_err_issue;
5e7ed085 19use rustc_session::Session;
dfeec247 20use rustc_span::symbol::{sym, Symbol};
5e7ed085 21use rustc_span::Span;
60c5eb7d
XL
22use std::num::NonZeroU32;
23
e74abb32 24#[derive(PartialEq, Clone, Copy, Debug)]
b039eaaf
SL
25pub enum StabilityLevel {
26 Unstable,
27 Stable,
28}
29
5bcae85e 30/// An entry in the `depr_map`.
923072b8 31#[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)]
5bcae85e
SL
32pub struct DeprecationEntry {
33 /// The metadata of the attribute associated with this entry.
34 pub attr: Deprecation,
9fa01778 35 /// The `DefId` where the attr was originally attached. `None` for non-local
5bcae85e 36 /// `DefId`'s.
94222f64 37 origin: Option<LocalDefId>,
5bcae85e
SL
38}
39
40impl DeprecationEntry {
94222f64
XL
41 pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry {
42 DeprecationEntry { attr, origin: Some(def_id) }
5bcae85e
SL
43 }
44
ea8adc8c 45 pub fn external(attr: Deprecation) -> DeprecationEntry {
dfeec247 46 DeprecationEntry { attr, origin: None }
5bcae85e
SL
47 }
48
49 pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
50 match (self.origin, other.origin) {
51 (Some(o1), Some(o2)) => o1 == o2,
dfeec247 52 _ => false,
5bcae85e
SL
53 }
54 }
55}
56
1a4d82fc 57/// A stability index, giving the stability level for items and methods.
5869c6ff 58#[derive(HashStable, Debug)]
5e7ed085 59pub struct Index {
62682a34
SL
60 /// This is mostly a cache, except the stabilities of local items
61 /// are filled by the annotator.
5e7ed085
FG
62 pub stab_map: FxHashMap<LocalDefId, Stability>,
63 pub const_stab_map: FxHashMap<LocalDefId, ConstStability>,
94222f64 64 pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
476ff2be
SL
65}
66
5e7ed085
FG
67impl Index {
68 pub fn local_stability(&self, def_id: LocalDefId) -> Option<Stability> {
94222f64 69 self.stab_map.get(&def_id).copied()
85aaf69f
SL
70 }
71
5e7ed085 72 pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<ConstStability> {
94222f64 73 self.const_stab_map.get(&def_id).copied()
60c5eb7d
XL
74 }
75
94222f64
XL
76 pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> {
77 self.depr_map.get(&def_id).cloned()
85aaf69f
SL
78 }
79}
80
416331ca 81pub fn report_unstable(
e74abb32
XL
82 sess: &Session,
83 feature: Symbol,
84 reason: Option<Symbol>,
60c5eb7d 85 issue: Option<NonZeroU32>,
3c0e092e 86 suggestion: Option<(Span, String, String, Applicability)>,
e74abb32
XL
87 is_soft: bool,
88 span: Span,
74b04a01 89 soft_handler: impl FnOnce(&'static Lint, Span, &str),
416331ca
XL
90) {
91 let msg = match reason {
92 Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
dfeec247 93 None => format!("use of unstable library feature '{}'", &feature),
416331ca
XL
94 };
95
5e7ed085
FG
96 if is_soft {
97 soft_handler(SOFT_UNSTABLE, span, &msg)
98 } else {
99 let mut err =
100 feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg);
101 if let Some((inner_types, ref msg, sugg, applicability)) = suggestion {
102 err.span_suggestion(inner_types, msg, sugg, applicability);
416331ca 103 }
5e7ed085 104 err.emit();
416331ca
XL
105 }
106}
107
9fa01778 108/// Checks whether an item marked with `deprecated(since="X")` is currently
0731742a 109/// deprecated (i.e., whether X is not greater than the current rustc version).
c295e0f8
XL
110pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
111 let is_since_rustc_version = depr.is_since_rustc_version;
a2a8927a 112 let since = depr.since.as_ref().map(Symbol::as_str);
c295e0f8 113
83c7162d 114 fn parse_version(ver: &str) -> Vec<u32> {
0731742a 115 // We ignore non-integer components of the version (e.g., "nightly").
83c7162d
XL
116 ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
117 }
118
fc512014 119 if !is_since_rustc_version {
04454e1e 120 // The `since` field doesn't have semantic purpose without `#![staged_api]`.
fc512014 121 return true;
83c7162d 122 }
fc512014
XL
123
124 if let Some(since) = since {
125 if since == "TBD" {
126 return false;
127 }
128
129 if let Some(rustc) = option_env!("CFG_RELEASE") {
130 let since: Vec<u32> = parse_version(&since);
131 let rustc: Vec<u32> = parse_version(rustc);
132 // We simply treat invalid `since` attributes as relating to a previous
133 // Rust version, thus always displaying the warning.
134 if since.len() != 3 {
135 return true;
136 }
137 return since <= rustc;
138 }
139 };
140
141 // Assume deprecation is in effect if "since" field is missing
142 // or if we can't determine the current Rust version.
143 true
83c7162d
XL
144}
145
416331ca 146pub fn deprecation_suggestion(
5e7ed085 147 diag: &mut Diagnostic,
3dfed10e 148 kind: &str,
dfeec247
XL
149 suggestion: Option<Symbol>,
150 span: Span,
416331ca
XL
151) {
152 if let Some(suggestion) = suggestion {
153 diag.span_suggestion(
154 span,
3dfed10e 155 &format!("replace the use of the deprecated {}", kind),
923072b8 156 suggestion,
416331ca
XL
157 Applicability::MachineApplicable,
158 );
159 }
160}
161
c295e0f8
XL
162fn deprecation_lint(is_in_effect: bool) -> &'static Lint {
163 if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE }
164}
165
166fn deprecation_message(
167 is_in_effect: bool,
168 since: Option<Symbol>,
169 note: Option<Symbol>,
170 kind: &str,
171 path: &str,
172) -> String {
173 let message = if is_in_effect {
174 format!("use of deprecated {} `{}`", kind, path)
416331ca 175 } else {
a2a8927a 176 let since = since.as_ref().map(Symbol::as_str);
c295e0f8 177
5099ac24 178 if since == Some("TBD") {
c295e0f8
XL
179 format!("use of {} `{}` that will be deprecated in a future Rust version", kind, path)
180 } else {
181 format!(
182 "use of {} `{}` that will be deprecated in future version {}",
183 kind,
184 path,
185 since.unwrap()
186 )
187 }
416331ca 188 };
c295e0f8
XL
189
190 match note {
3dfed10e
XL
191 Some(reason) => format!("{}: {}", message, reason),
192 None => message,
c295e0f8
XL
193 }
194}
195
196pub fn deprecation_message_and_lint(
197 depr: &Deprecation,
198 kind: &str,
199 path: &str,
200) -> (String, &'static Lint) {
201 let is_in_effect = deprecation_in_effect(depr);
202 (
203 deprecation_message(is_in_effect, depr.since, depr.note, kind, path),
204 deprecation_lint(is_in_effect),
205 )
416331ca
XL
206}
207
a2a8927a 208pub fn early_report_deprecation<'a>(
dfeec247 209 lint_buffer: &'a mut LintBuffer,
416331ca
XL
210 message: &str,
211 suggestion: Option<Symbol>,
212 lint: &'static Lint,
213 span: Span,
fc512014 214 node_id: NodeId,
416331ca 215) {
dfeec247 216 if span.in_derive_expansion() {
416331ca
XL
217 return;
218 }
219
220 let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
fc512014 221 lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag);
416331ca
XL
222}
223
224fn late_report_deprecation(
225 tcx: TyCtxt<'_>,
226 message: &str,
227 suggestion: Option<Symbol>,
228 lint: &'static Lint,
229 span: Span,
136023e0 230 method_span: Option<Span>,
416331ca 231 hir_id: HirId,
3dfed10e 232 def_id: DefId,
416331ca 233) {
dfeec247 234 if span.in_derive_expansion() {
416331ca
XL
235 return;
236 }
136023e0
XL
237 let method_span = method_span.unwrap_or(span);
238 tcx.struct_span_lint_hir(lint, hir_id, method_span, |lint| {
74b04a01
XL
239 let mut diag = lint.build(message);
240 if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
3dfed10e 241 let kind = tcx.def_kind(def_id).descr(def_id);
136023e0 242 deprecation_suggestion(&mut diag, kind, suggestion, method_span);
74b04a01 243 }
5e7ed085 244 diag.emit();
74b04a01 245 });
416331ca
XL
246}
247
0531ce1d
XL
248/// Result of `TyCtxt::eval_stability`.
249pub enum EvalResult {
250 /// We can use the item because it is stable or we provided the
251 /// corresponding feature gate.
252 Allow,
253 /// We cannot use the item because it is unstable and we did not provide the
254 /// corresponding feature gate.
3c0e092e
XL
255 Deny {
256 feature: Symbol,
257 reason: Option<Symbol>,
258 issue: Option<NonZeroU32>,
259 suggestion: Option<(Span, String, String, Applicability)>,
260 is_soft: bool,
261 },
0531ce1d
XL
262 /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
263 Unmarked,
264}
265
dfeec247 266// See issue #38412.
29967ef6
XL
267fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
268 if tcx.def_kind(def_id) == DefKind::TyParam {
269 // Have no visibility, considered public for the purpose of this check.
270 return false;
dfeec247 271 }
29967ef6 272 match tcx.visibility(def_id) {
dfeec247
XL
273 // Must check stability for `pub` items.
274 ty::Visibility::Public => false,
476ff2be 275
dfeec247
XL
276 // These are not visible outside crate; therefore
277 // stability markers are irrelevant, if even present.
278 ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
476ff2be 279 }
dfeec247 280}
476ff2be 281
3c0e092e
XL
282// See issue #83250.
283fn suggestion_for_allocator_api(
284 tcx: TyCtxt<'_>,
285 def_id: DefId,
286 span: Span,
287 feature: Symbol,
288) -> Option<(Span, String, String, Applicability)> {
289 if feature == sym::allocator_api {
04454e1e 290 if let Some(trait_) = tcx.opt_parent(def_id) {
3c0e092e
XL
291 if tcx.is_diagnostic_item(sym::Vec, trait_) {
292 let sm = tcx.sess.parse_sess.source_map();
293 let inner_types = sm.span_extend_to_prev_char(span, '<', true);
294 if let Ok(snippet) = sm.span_to_snippet(inner_types) {
295 return Some((
296 inner_types,
297 "consider wrapping the inner types in tuple".to_string(),
298 format!("({})", snippet),
299 Applicability::MaybeIncorrect,
300 ));
301 }
302 }
303 }
304 }
305 None
306}
307
923072b8
FG
308/// An override option for eval_stability.
309pub enum AllowUnstable {
310 /// Don't emit an unstable error for the item
311 Yes,
312 /// Handle the item normally
313 No,
314}
315
dfeec247 316impl<'tcx> TyCtxt<'tcx> {
0531ce1d
XL
317 /// Evaluates the stability of an item.
318 ///
319 /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
320 /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
321 /// unstable feature otherwise.
322 ///
323 /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
324 /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
325 /// `id`.
17df50a5
XL
326 pub fn eval_stability(
327 self,
328 def_id: DefId,
329 id: Option<HirId>,
330 span: Span,
331 method_span: Option<Span>,
923072b8
FG
332 ) -> EvalResult {
333 self.eval_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
334 }
335
336 /// Evaluates the stability of an item.
337 ///
338 /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
339 /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
340 /// unstable feature otherwise.
341 ///
342 /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
343 /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
344 /// `id`.
345 ///
346 /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally.
347 pub fn eval_stability_allow_unstable(
348 self,
349 def_id: DefId,
350 id: Option<HirId>,
351 span: Span,
352 method_span: Option<Span>,
353 allow_unstable: AllowUnstable,
17df50a5 354 ) -> EvalResult {
476ff2be 355 // Deprecated attributes apply in-crate and cross-crate.
0531ce1d
XL
356 if let Some(id) = id {
357 if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
5099ac24 358 let parent_def_id = self.hir().get_parent_item(id);
dfeec247 359 let skip = self
f9f354fc 360 .lookup_deprecation_entry(parent_def_id.to_def_id())
dfeec247 361 .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
0731742a 362
3dfed10e
XL
363 // #[deprecated] doesn't emit a notice if we're not on the
364 // topmost deprecation. For example, if a struct is deprecated,
365 // the use of a field won't be linted.
366 //
04454e1e 367 // With #![staged_api], we want to emit down the whole
3dfed10e 368 // hierarchy.
c295e0f8
XL
369 let depr_attr = &depr_entry.attr;
370 if !skip || depr_attr.is_since_rustc_version {
371 // Calculating message for lint involves calling `self.def_path_str`.
372 // Which by default to calculate visible path will invoke expensive `visible_parent_map` query.
373 // So we skip message calculation altogether, if lint is allowed.
374 let is_in_effect = deprecation_in_effect(depr_attr);
375 let lint = deprecation_lint(is_in_effect);
376 if self.lint_level_at_node(lint, id).0 != Level::Allow {
5e7ed085 377 let def_path = with_no_trimmed_paths!(self.def_path_str(def_id));
c295e0f8
XL
378 let def_kind = self.def_kind(def_id).descr(def_id);
379
380 late_report_deprecation(
381 self,
382 &deprecation_message(
383 is_in_effect,
384 depr_attr.since,
385 depr_attr.note,
386 def_kind,
5e7ed085 387 &def_path,
c295e0f8
XL
388 ),
389 depr_attr.suggestion,
390 lint,
391 span,
392 method_span,
393 id,
394 def_id,
395 );
396 }
0531ce1d 397 }
476ff2be 398 };
476ff2be
SL
399 }
400
04454e1e 401 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
476ff2be 402 if !is_staged_api {
0531ce1d 403 return EvalResult::Allow;
9cc50fc6 404 }
476ff2be
SL
405
406 let stability = self.lookup_stability(def_id);
dfeec247
XL
407 debug!(
408 "stability: \
409 inspecting def_id={:?} span={:?} of stability={:?}",
410 def_id, span, stability
411 );
476ff2be 412
85aaf69f 413 // Only the cross-crate scenario matters when checking unstable APIs
476ff2be 414 let cross_crate = !def_id.is_local();
b039eaaf 415 if !cross_crate {
0531ce1d 416 return EvalResult::Allow;
b039eaaf
SL
417 }
418
0731742a 419 // Issue #38412: private items lack stability markers.
dfeec247 420 if skip_stability_check_due_to_privacy(self, def_id) {
0531ce1d 421 return EvalResult::Allow;
476ff2be 422 }
85aaf69f 423
476ff2be 424 match stability {
5e7ed085 425 Some(Stability {
416331ca
XL
426 level: attr::Unstable { reason, issue, is_soft }, feature, ..
427 }) => {
48663c56 428 if span.allows_unstable(feature) {
9fa01778
XL
429 debug!("stability: skipping span={:?} since it is internal", span);
430 return EvalResult::Allow;
431 }
5e7ed085 432 if self.features().active(feature) {
0531ce1d 433 return EvalResult::Allow;
85aaf69f 434 }
7cac9316
XL
435
436 // When we're compiling the compiler itself we may pull in
437 // crates from crates.io, but those crates may depend on other
438 // crates also pulled in from crates.io. We want to ideally be
439 // able to compile everything without requiring upstream
440 // modifications, so in the case that this looks like a
0731742a 441 // `rustc_private` crate (e.g., a compiler crate) and we also have
7cac9316
XL
442 // the `-Z force-unstable-if-unmarked` flag present (we're
443 // compiling a compiler crate), then let this missing feature
444 // annotation slide.
60c5eb7d 445 if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
7cac9316 446 if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
0531ce1d 447 return EvalResult::Allow;
7cac9316
XL
448 }
449 }
450
923072b8
FG
451 if matches!(allow_unstable, AllowUnstable::Yes) {
452 return EvalResult::Allow;
453 }
454
3c0e092e
XL
455 let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
456 EvalResult::Deny { feature, reason, issue, suggestion, is_soft }
0531ce1d
XL
457 }
458 Some(_) => {
459 // Stable APIs are always ok to call and deprecated APIs are
460 // handled by the lint emitting logic above.
461 EvalResult::Allow
462 }
dfeec247 463 None => EvalResult::Unmarked,
0531ce1d
XL
464 }
465 }
466
467 /// Checks if an item is stable or error out.
468 ///
469 /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
470 /// exist, emits an error.
471 ///
1b1a35ee
XL
472 /// This function will also check if the item is deprecated.
473 /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
17df50a5
XL
474 pub fn check_stability(
475 self,
476 def_id: DefId,
477 id: Option<HirId>,
478 span: Span,
479 method_span: Option<Span>,
480 ) {
923072b8
FG
481 self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
482 }
483
484 /// Checks if an item is stable or error out.
485 ///
486 /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
487 /// exist, emits an error.
488 ///
489 /// This function will also check if the item is deprecated.
490 /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
491 ///
492 /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally.
493 pub fn check_stability_allow_unstable(
494 self,
495 def_id: DefId,
496 id: Option<HirId>,
497 span: Span,
498 method_span: Option<Span>,
499 allow_unstable: AllowUnstable,
500 ) {
501 self.check_optional_stability(
502 def_id,
503 id,
504 span,
505 method_span,
506 allow_unstable,
507 |span, def_id| {
508 // The API could be uncallable for other reasons, for example when a private module
509 // was referenced.
510 self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
511 },
512 )
1b1a35ee
XL
513 }
514
515 /// Like `check_stability`, except that we permit items to have custom behaviour for
516 /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary
517 /// for default generic parameters, which only have stability attributes if they were
518 /// added after the type on which they're defined.
519 pub fn check_optional_stability(
520 self,
521 def_id: DefId,
522 id: Option<HirId>,
523 span: Span,
17df50a5 524 method_span: Option<Span>,
923072b8 525 allow_unstable: AllowUnstable,
29967ef6 526 unmarked: impl FnOnce(Span, DefId),
1b1a35ee 527 ) {
74b04a01
XL
528 let soft_handler = |lint, span, msg: &_| {
529 self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
5e7ed085 530 lint.build(msg).emit();
74b04a01
XL
531 })
532 };
923072b8 533 match self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable) {
0531ce1d 534 EvalResult::Allow => {}
3c0e092e
XL
535 EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
536 self.sess,
537 feature,
538 reason,
539 issue,
540 suggestion,
541 is_soft,
542 span,
543 soft_handler,
544 ),
1b1a35ee 545 EvalResult::Unmarked => unmarked(span, def_id),
85aaf69f
SL
546 }
547 }
85aaf69f 548
a7813a04 549 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
5bcae85e
SL
550 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
551 }
1a4d82fc 552}