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