1use std::num::NonZero;
5
6use rustc_ast::NodeId;
7use rustc_attr_data_structures::{
8 self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability,
9};
10use rustc_data_structures::unord::UnordMap;
11use rustc_errors::{Applicability, Diag, EmissionGuarantee};
12use rustc_feature::GateIssue;
13use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
14use rustc_hir::{self as hir, HirId};
15use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic};
16use rustc_session::Session;
17use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
18use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
19use rustc_session::parse::feature_err_issue;
20use rustc_span::{Span, Symbol, sym};
21use tracing::debug;
22
23pub use self::StabilityLevel::*;
24use crate::ty::TyCtxt;
25use crate::ty::print::with_no_trimmed_paths;
26
27#[derive(PartialEq, Clone, Copy, Debug)]
28pub enum StabilityLevel {
29 Unstable,
30 Stable,
31}
32
33#[derive(Copy, Clone)]
34pub enum UnstableKind {
35 Regular,
37 Const(Span),
39}
40
41#[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)]
43pub struct DeprecationEntry {
44 pub attr: Deprecation,
46 origin: Option<LocalDefId>,
49}
50
51impl DeprecationEntry {
52 pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry {
53 DeprecationEntry { attr, origin: Some(def_id) }
54 }
55
56 pub fn external(attr: Deprecation) -> DeprecationEntry {
57 DeprecationEntry { attr, origin: None }
58 }
59
60 pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
61 match (self.origin, other.origin) {
62 (Some(o1), Some(o2)) => o1 == o2,
63 _ => false,
64 }
65 }
66}
67
68#[derive(HashStable, Debug)]
70pub struct Index {
71 pub stab_map: LocalDefIdMap<Stability>,
74 pub const_stab_map: LocalDefIdMap<ConstStability>,
75 pub default_body_stab_map: LocalDefIdMap<DefaultBodyStability>,
76 pub depr_map: LocalDefIdMap<DeprecationEntry>,
77 pub implications: UnordMap<Symbol, Symbol>,
90}
91
92impl Index {
93 pub fn local_stability(&self, def_id: LocalDefId) -> Option<Stability> {
94 self.stab_map.get(&def_id).copied()
95 }
96
97 pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<ConstStability> {
98 self.const_stab_map.get(&def_id).copied()
99 }
100
101 pub fn local_default_body_stability(&self, def_id: LocalDefId) -> Option<DefaultBodyStability> {
102 self.default_body_stab_map.get(&def_id).copied()
103 }
104
105 pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> {
106 self.depr_map.get(&def_id).cloned()
107 }
108}
109
110pub fn report_unstable(
111 sess: &Session,
112 feature: Symbol,
113 reason: Option<Symbol>,
114 issue: Option<NonZero<u32>>,
115 suggestion: Option<(Span, String, String, Applicability)>,
116 is_soft: bool,
117 span: Span,
118 soft_handler: impl FnOnce(&'static Lint, Span, String),
119 kind: UnstableKind,
120) {
121 let qual = match kind {
122 UnstableKind::Regular => "",
123 UnstableKind::Const(_) => " const",
124 };
125
126 let msg = match reason {
127 Some(r) => format!("use of unstable{qual} library feature `{feature}`: {r}"),
128 None => format!("use of unstable{qual} library feature `{feature}`"),
129 };
130
131 if is_soft {
132 soft_handler(SOFT_UNSTABLE, span, msg)
133 } else {
134 let mut err = feature_err_issue(sess, feature, span, GateIssue::Library(issue), msg);
135 if let Some((inner_types, msg, sugg, applicability)) = suggestion {
136 err.span_suggestion(inner_types, msg, sugg, applicability);
137 }
138 if let UnstableKind::Const(kw) = kind {
139 err.span_label(kw, "trait is not stable as const yet");
140 }
141 err.emit();
142 }
143}
144
145fn deprecation_lint(is_in_effect: bool) -> &'static Lint {
146 if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE }
147}
148
149#[derive(Subdiagnostic)]
150#[suggestion(
151 middle_deprecated_suggestion,
152 code = "{suggestion}",
153 style = "verbose",
154 applicability = "machine-applicable"
155)]
156pub struct DeprecationSuggestion {
157 #[primary_span]
158 pub span: Span,
159
160 pub kind: String,
161 pub suggestion: Symbol,
162}
163
164pub struct Deprecated {
165 pub sub: Option<DeprecationSuggestion>,
166
167 pub kind: String,
169 pub path: String,
170 pub note: Option<Symbol>,
171 pub since_kind: DeprecatedSinceKind,
172}
173
174impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecated {
175 fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
176 diag.primary_message(match &self.since_kind {
177 DeprecatedSinceKind::InEffect => crate::fluent_generated::middle_deprecated,
178 DeprecatedSinceKind::InFuture => crate::fluent_generated::middle_deprecated_in_future,
179 DeprecatedSinceKind::InVersion(_) => {
180 crate::fluent_generated::middle_deprecated_in_version
181 }
182 });
183 diag.arg("kind", self.kind);
184 diag.arg("path", self.path);
185 if let DeprecatedSinceKind::InVersion(version) = self.since_kind {
186 diag.arg("version", version);
187 }
188 if let Some(note) = self.note {
189 diag.arg("has_note", true);
190 diag.arg("note", note);
191 } else {
192 diag.arg("has_note", false);
193 }
194 if let Some(sub) = self.sub {
195 diag.subdiagnostic(sub);
196 }
197 }
198}
199
200fn deprecated_since_kind(is_in_effect: bool, since: DeprecatedSince) -> DeprecatedSinceKind {
201 if is_in_effect {
202 DeprecatedSinceKind::InEffect
203 } else {
204 match since {
205 DeprecatedSince::RustcVersion(version) => {
206 DeprecatedSinceKind::InVersion(version.to_string())
207 }
208 DeprecatedSince::Future => DeprecatedSinceKind::InFuture,
209 DeprecatedSince::NonStandard(_)
210 | DeprecatedSince::Unspecified
211 | DeprecatedSince::Err => {
212 unreachable!("this deprecation is always in effect; {since:?}")
213 }
214 }
215 }
216}
217
218pub fn early_report_macro_deprecation(
219 lint_buffer: &mut LintBuffer,
220 depr: &Deprecation,
221 span: Span,
222 node_id: NodeId,
223 path: String,
224) {
225 if span.in_derive_expansion() {
226 return;
227 }
228
229 let is_in_effect = depr.is_in_effect();
230 let diag = BuiltinLintDiag::DeprecatedMacro {
231 suggestion: depr.suggestion,
232 suggestion_span: span,
233 note: depr.note,
234 path,
235 since_kind: deprecated_since_kind(is_in_effect, depr.since),
236 };
237 lint_buffer.buffer_lint(deprecation_lint(is_in_effect), node_id, span, diag);
238}
239
240fn late_report_deprecation(
241 tcx: TyCtxt<'_>,
242 depr: &Deprecation,
243 span: Span,
244 method_span: Option<Span>,
245 hir_id: HirId,
246 def_id: DefId,
247) {
248 if span.in_derive_expansion() {
249 return;
250 }
251
252 let is_in_effect = depr.is_in_effect();
253 let lint = deprecation_lint(is_in_effect);
254
255 if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
259 return;
260 }
261
262 let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id));
263 let def_kind = tcx.def_descr(def_id);
264
265 let method_span = method_span.unwrap_or(span);
266 let suggestion =
267 if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { depr.suggestion } else { None };
268 let diag = Deprecated {
269 sub: suggestion.map(|suggestion| DeprecationSuggestion {
270 span: method_span,
271 kind: def_kind.to_owned(),
272 suggestion,
273 }),
274 kind: def_kind.to_owned(),
275 path: def_path,
276 note: depr.note,
277 since_kind: deprecated_since_kind(is_in_effect, depr.since),
278 };
279 tcx.emit_node_span_lint(lint, hir_id, method_span, diag);
280}
281
282pub enum EvalResult {
284 Allow,
287 Deny {
290 feature: Symbol,
291 reason: Option<Symbol>,
292 issue: Option<NonZero<u32>>,
293 suggestion: Option<(Span, String, String, Applicability)>,
294 is_soft: bool,
295 },
296 Unmarked,
298}
299
300fn suggestion_for_allocator_api(
302 tcx: TyCtxt<'_>,
303 def_id: DefId,
304 span: Span,
305 feature: Symbol,
306) -> Option<(Span, String, String, Applicability)> {
307 if feature == sym::allocator_api {
308 if let Some(trait_) = tcx.opt_parent(def_id) {
309 if tcx.is_diagnostic_item(sym::Vec, trait_) {
310 let sm = tcx.sess.psess.source_map();
311 let inner_types = sm.span_extend_to_prev_char(span, '<', true);
312 if let Ok(snippet) = sm.span_to_snippet(inner_types) {
313 return Some((
314 inner_types,
315 "consider wrapping the inner types in tuple".to_string(),
316 format!("({snippet})"),
317 Applicability::MaybeIncorrect,
318 ));
319 }
320 }
321 }
322 }
323 None
324}
325
326pub enum AllowUnstable {
328 Yes,
330 No,
332}
333
334impl<'tcx> TyCtxt<'tcx> {
335 pub fn eval_stability(
345 self,
346 def_id: DefId,
347 id: Option<HirId>,
348 span: Span,
349 method_span: Option<Span>,
350 ) -> EvalResult {
351 self.eval_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
352 }
353
354 pub fn eval_stability_allow_unstable(
366 self,
367 def_id: DefId,
368 id: Option<HirId>,
369 span: Span,
370 method_span: Option<Span>,
371 allow_unstable: AllowUnstable,
372 ) -> EvalResult {
373 if let Some(id) = id {
375 if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
376 let parent_def_id = self.hir_get_parent_item(id);
377 let skip = self
378 .lookup_deprecation_entry(parent_def_id.to_def_id())
379 .is_some_and(|parent_depr| parent_depr.same_origin(&depr_entry));
380
381 let depr_attr = &depr_entry.attr;
388 if !skip || depr_attr.is_since_rustc_version() {
389 late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
390 }
391 };
392 }
393
394 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
395 if !is_staged_api {
396 return EvalResult::Allow;
397 }
398
399 let cross_crate = !def_id.is_local();
401 if !cross_crate {
402 return EvalResult::Allow;
403 }
404
405 let stability = self.lookup_stability(def_id);
406 debug!(
407 "stability: \
408 inspecting def_id={:?} span={:?} of stability={:?}",
409 def_id, span, stability
410 );
411
412 match stability {
413 Some(Stability {
414 level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by },
415 feature,
416 ..
417 }) => {
418 if span.allows_unstable(feature) {
419 debug!("stability: skipping span={:?} since it is internal", span);
420 return EvalResult::Allow;
421 }
422 if self.features().enabled(feature) {
423 return EvalResult::Allow;
424 }
425
426 if let Some(implied_by) = implied_by
430 && self.features().enabled(implied_by)
431 {
432 return EvalResult::Allow;
433 }
434
435 if feature == sym::rustc_private
445 && issue == NonZero::new(27812)
446 && self.sess.opts.unstable_opts.force_unstable_if_unmarked
447 {
448 return EvalResult::Allow;
449 }
450
451 if matches!(allow_unstable, AllowUnstable::Yes) {
452 return EvalResult::Allow;
453 }
454
455 let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
456 EvalResult::Deny {
457 feature,
458 reason: reason.to_opt_reason(),
459 issue,
460 suggestion,
461 is_soft,
462 }
463 }
464 Some(_) => {
465 EvalResult::Allow
468 }
469 None => EvalResult::Unmarked,
470 }
471 }
472
473 pub fn eval_default_body_stability(self, def_id: DefId, span: Span) -> EvalResult {
479 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
480 if !is_staged_api {
481 return EvalResult::Allow;
482 }
483
484 let cross_crate = !def_id.is_local();
486 if !cross_crate {
487 return EvalResult::Allow;
488 }
489
490 let stability = self.lookup_default_body_stability(def_id);
491 debug!(
492 "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}"
493 );
494
495 match stability {
496 Some(DefaultBodyStability {
497 level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. },
498 feature,
499 }) => {
500 if span.allows_unstable(feature) {
501 debug!("body stability: skipping span={:?} since it is internal", span);
502 return EvalResult::Allow;
503 }
504 if self.features().enabled(feature) {
505 return EvalResult::Allow;
506 }
507
508 EvalResult::Deny {
509 feature,
510 reason: reason.to_opt_reason(),
511 issue,
512 suggestion: None,
513 is_soft,
514 }
515 }
516 Some(_) => {
517 EvalResult::Allow
519 }
520 None => EvalResult::Unmarked,
521 }
522 }
523
524 pub fn check_stability(
534 self,
535 def_id: DefId,
536 id: Option<HirId>,
537 span: Span,
538 method_span: Option<Span>,
539 ) -> bool {
540 self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
541 }
542
543 pub fn check_stability_allow_unstable(
555 self,
556 def_id: DefId,
557 id: Option<HirId>,
558 span: Span,
559 method_span: Option<Span>,
560 allow_unstable: AllowUnstable,
561 ) -> bool {
562 self.check_optional_stability(
563 def_id,
564 id,
565 span,
566 method_span,
567 allow_unstable,
568 |span, def_id| {
569 self.dcx().span_delayed_bug(span, format!("encountered unmarked API: {def_id:?}"));
572 },
573 )
574 }
575
576 pub fn check_optional_stability(
583 self,
584 def_id: DefId,
585 id: Option<HirId>,
586 span: Span,
587 method_span: Option<Span>,
588 allow_unstable: AllowUnstable,
589 unmarked: impl FnOnce(Span, DefId),
590 ) -> bool {
591 let soft_handler = |lint, span, msg: String| {
592 self.node_span_lint(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
593 lint.primary_message(msg);
594 })
595 };
596 let eval_result =
597 self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable);
598 let is_allowed = matches!(eval_result, EvalResult::Allow);
599 match eval_result {
600 EvalResult::Allow => {}
601 EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
602 self.sess,
603 feature,
604 reason,
605 issue,
606 suggestion,
607 is_soft,
608 span,
609 soft_handler,
610 UnstableKind::Regular,
611 ),
612 EvalResult::Unmarked => unmarked(span, def_id),
613 }
614
615 is_allowed
616 }
617
618 pub fn check_const_stability(self, def_id: DefId, span: Span, const_kw_span: Span) {
626 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
627 if !is_staged_api {
628 return;
629 }
630
631 let cross_crate = !def_id.is_local();
633 if !cross_crate {
634 return;
635 }
636
637 let stability = self.lookup_const_stability(def_id);
638 debug!(
639 "stability: \
640 inspecting def_id={:?} span={:?} of stability={:?}",
641 def_id, span, stability
642 );
643
644 match stability {
645 Some(ConstStability {
646 level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
647 feature,
648 ..
649 }) => {
650 assert!(!is_soft);
651
652 if span.allows_unstable(feature) {
653 debug!("body stability: skipping span={:?} since it is internal", span);
654 return;
655 }
656 if self.features().enabled(feature) {
657 return;
658 }
659
660 if let Some(implied_by) = implied_by
664 && self.features().enabled(implied_by)
665 {
666 return;
667 }
668
669 report_unstable(
670 self.sess,
671 feature,
672 reason.to_opt_reason(),
673 issue,
674 None,
675 false,
676 span,
677 |_, _, _| {},
678 UnstableKind::Const(const_kw_span),
679 );
680 }
681 Some(_) | None => {}
682 }
683 }
684
685 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
686 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
687 }
688}