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