1use std::num::NonZero;
5
6use rustc_ast::NodeId;
7use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer};
8use rustc_feature::GateIssue;
9use rustc_hir::attrs::{DeprecatedSince, Deprecation};
10use rustc_hir::def_id::{DefId, LocalDefId};
11use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stability};
12use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic};
13use rustc_session::Session;
14use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
15use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint};
16use rustc_session::parse::feature_err_issue;
17use rustc_span::{Span, Symbol, sym};
18use tracing::debug;
19
20pub use self::StabilityLevel::*;
21use crate::ty::TyCtxt;
22use crate::ty::print::with_no_trimmed_paths;
23
24#[derive(PartialEq, Clone, Copy, Debug)]
25pub enum StabilityLevel {
26 Unstable,
27 Stable,
28}
29
30#[derive(Copy, Clone)]
31pub enum UnstableKind {
32 Regular,
34 Const(Span),
36}
37
38#[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)]
40pub struct DeprecationEntry {
41 pub attr: Deprecation,
43 origin: Option<LocalDefId>,
46}
47
48impl DeprecationEntry {
49 pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry {
50 DeprecationEntry { attr, origin: Some(def_id) }
51 }
52
53 pub fn external(attr: Deprecation) -> DeprecationEntry {
54 DeprecationEntry { attr, origin: None }
55 }
56
57 pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
58 match (self.origin, other.origin) {
59 (Some(o1), Some(o2)) => o1 == o2,
60 _ => false,
61 }
62 }
63}
64
65pub fn report_unstable(
66 sess: &Session,
67 feature: Symbol,
68 reason: Option<Symbol>,
69 issue: Option<NonZero<u32>>,
70 suggestion: Option<(Span, String, String, Applicability)>,
71 is_soft: bool,
72 span: Span,
73 soft_handler: impl FnOnce(&'static Lint, Span, String),
74 kind: UnstableKind,
75) {
76 let qual = match kind {
77 UnstableKind::Regular => "",
78 UnstableKind::Const(_) => " const",
79 };
80
81 let msg = match reason {
82 Some(r) => format!("use of unstable{qual} library feature `{feature}`: {r}"),
83 None => format!("use of unstable{qual} library feature `{feature}`"),
84 };
85
86 if is_soft {
87 soft_handler(SOFT_UNSTABLE, span, msg)
88 } else {
89 let mut err = feature_err_issue(sess, feature, span, GateIssue::Library(issue), msg);
90 if let Some((inner_types, msg, sugg, applicability)) = suggestion {
91 err.span_suggestion(inner_types, msg, sugg, applicability);
92 }
93 if let UnstableKind::Const(kw) = kind {
94 err.span_label(kw, "trait is not stable as const yet");
95 }
96 err.emit();
97 }
98}
99
100fn deprecation_lint(is_in_effect: bool) -> &'static Lint {
101 if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE }
102}
103
104#[derive(Subdiagnostic)]
105#[suggestion(
106 middle_deprecated_suggestion,
107 code = "{suggestion}",
108 style = "verbose",
109 applicability = "machine-applicable"
110)]
111pub struct DeprecationSuggestion {
112 #[primary_span]
113 pub span: Span,
114
115 pub kind: String,
116 pub suggestion: Symbol,
117}
118
119pub struct Deprecated {
120 pub sub: Option<DeprecationSuggestion>,
121
122 pub kind: String,
124 pub path: String,
125 pub note: Option<Symbol>,
126 pub since_kind: DeprecatedSinceKind,
127}
128
129impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecated {
130 fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
131 diag.primary_message(match &self.since_kind {
132 DeprecatedSinceKind::InEffect => crate::fluent_generated::middle_deprecated,
133 DeprecatedSinceKind::InFuture => crate::fluent_generated::middle_deprecated_in_future,
134 DeprecatedSinceKind::InVersion(_) => {
135 crate::fluent_generated::middle_deprecated_in_version
136 }
137 });
138 diag.arg("kind", self.kind);
139 diag.arg("path", self.path);
140 if let DeprecatedSinceKind::InVersion(version) = self.since_kind {
141 diag.arg("version", version);
142 }
143 if let Some(note) = self.note {
144 diag.arg("has_note", true);
145 diag.arg("note", note);
146 } else {
147 diag.arg("has_note", false);
148 }
149 if let Some(sub) = self.sub {
150 diag.subdiagnostic(sub);
151 }
152 }
153}
154
155fn deprecated_since_kind(is_in_effect: bool, since: DeprecatedSince) -> DeprecatedSinceKind {
156 if is_in_effect {
157 DeprecatedSinceKind::InEffect
158 } else {
159 match since {
160 DeprecatedSince::RustcVersion(version) => {
161 DeprecatedSinceKind::InVersion(version.to_string())
162 }
163 DeprecatedSince::Future => DeprecatedSinceKind::InFuture,
164 DeprecatedSince::NonStandard(_)
165 | DeprecatedSince::Unspecified
166 | DeprecatedSince::Err => {
167 unreachable!("this deprecation is always in effect; {since:?}")
168 }
169 }
170 }
171}
172
173pub fn early_report_macro_deprecation(
174 lint_buffer: &mut LintBuffer,
175 depr: &Deprecation,
176 span: Span,
177 node_id: NodeId,
178 path: String,
179) {
180 if span.in_derive_expansion() {
181 return;
182 }
183
184 let is_in_effect = depr.is_in_effect();
185 let diag = BuiltinLintDiag::DeprecatedMacro {
186 suggestion: depr.suggestion,
187 suggestion_span: span,
188 note: depr.note,
189 path,
190 since_kind: deprecated_since_kind(is_in_effect, depr.since),
191 };
192 lint_buffer.buffer_lint(deprecation_lint(is_in_effect), node_id, span, diag);
193}
194
195fn late_report_deprecation(
196 tcx: TyCtxt<'_>,
197 depr: &Deprecation,
198 span: Span,
199 method_span: Option<Span>,
200 hir_id: HirId,
201 def_id: DefId,
202) {
203 if span.in_derive_expansion() {
204 return;
205 }
206
207 let is_in_effect = depr.is_in_effect();
208 let lint = deprecation_lint(is_in_effect);
209
210 if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
214 return;
215 }
216
217 let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id));
218 let def_kind = tcx.def_descr(def_id);
219
220 let method_span = method_span.unwrap_or(span);
221 let suggestion =
222 if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { depr.suggestion } else { None };
223 let diag = Deprecated {
224 sub: suggestion.map(|suggestion| DeprecationSuggestion {
225 span: method_span,
226 kind: def_kind.to_owned(),
227 suggestion,
228 }),
229 kind: def_kind.to_owned(),
230 path: def_path,
231 note: depr.note,
232 since_kind: deprecated_since_kind(is_in_effect, depr.since),
233 };
234 tcx.emit_node_span_lint(lint, hir_id, method_span, diag);
235}
236
237pub enum EvalResult {
239 Allow,
242 Deny {
245 feature: Symbol,
246 reason: Option<Symbol>,
247 issue: Option<NonZero<u32>>,
248 suggestion: Option<(Span, String, String, Applicability)>,
249 is_soft: bool,
250 },
251 Unmarked,
253}
254
255fn suggestion_for_allocator_api(
257 tcx: TyCtxt<'_>,
258 def_id: DefId,
259 span: Span,
260 feature: Symbol,
261) -> Option<(Span, String, String, Applicability)> {
262 if feature == sym::allocator_api {
263 if let Some(trait_) = tcx.opt_parent(def_id) {
264 if tcx.is_diagnostic_item(sym::Vec, trait_) {
265 let sm = tcx.sess.psess.source_map();
266 let inner_types = sm.span_extend_to_prev_char(span, '<', true);
267 if let Ok(snippet) = sm.span_to_snippet(inner_types) {
268 return Some((
269 inner_types,
270 "consider wrapping the inner types in tuple".to_string(),
271 format!("({snippet})"),
272 Applicability::MaybeIncorrect,
273 ));
274 }
275 }
276 }
277 }
278 None
279}
280
281pub enum AllowUnstable {
283 Yes,
285 No,
287}
288
289impl<'tcx> TyCtxt<'tcx> {
290 pub fn eval_stability(
300 self,
301 def_id: DefId,
302 id: Option<HirId>,
303 span: Span,
304 method_span: Option<Span>,
305 ) -> EvalResult {
306 self.eval_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
307 }
308
309 pub fn eval_stability_allow_unstable(
321 self,
322 def_id: DefId,
323 id: Option<HirId>,
324 span: Span,
325 method_span: Option<Span>,
326 allow_unstable: AllowUnstable,
327 ) -> EvalResult {
328 if let Some(id) = id {
330 if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
331 let parent_def_id = self.hir_get_parent_item(id);
332 let skip = self
333 .lookup_deprecation_entry(parent_def_id.to_def_id())
334 .is_some_and(|parent_depr| parent_depr.same_origin(&depr_entry));
335
336 let depr_attr = &depr_entry.attr;
343 if !skip || depr_attr.is_since_rustc_version() {
344 late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
345 }
346 };
347 }
348
349 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
350 if !is_staged_api {
351 return EvalResult::Allow;
352 }
353
354 let cross_crate = !def_id.is_local();
356 if !cross_crate {
357 return EvalResult::Allow;
358 }
359
360 let stability = self.lookup_stability(def_id);
361 debug!(
362 "stability: \
363 inspecting def_id={:?} span={:?} of stability={:?}",
364 def_id, span, stability
365 );
366
367 match stability {
368 Some(Stability {
369 level: hir::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
370 feature,
371 ..
372 }) => {
373 if span.allows_unstable(feature) {
374 debug!("stability: skipping span={:?} since it is internal", span);
375 return EvalResult::Allow;
376 }
377 if self.features().enabled(feature) {
378 return EvalResult::Allow;
379 }
380
381 if let Some(implied_by) = implied_by
385 && self.features().enabled(implied_by)
386 {
387 return EvalResult::Allow;
388 }
389
390 if feature == sym::rustc_private
400 && issue == NonZero::new(27812)
401 && self.sess.opts.unstable_opts.force_unstable_if_unmarked
402 {
403 return EvalResult::Allow;
404 }
405
406 if matches!(allow_unstable, AllowUnstable::Yes) {
407 return EvalResult::Allow;
408 }
409
410 let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
411 EvalResult::Deny {
412 feature,
413 reason: reason.to_opt_reason(),
414 issue,
415 suggestion,
416 is_soft,
417 }
418 }
419 Some(_) => {
420 EvalResult::Allow
423 }
424 None => EvalResult::Unmarked,
425 }
426 }
427
428 pub fn eval_default_body_stability(self, def_id: DefId, span: Span) -> EvalResult {
434 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
435 if !is_staged_api {
436 return EvalResult::Allow;
437 }
438
439 let cross_crate = !def_id.is_local();
441 if !cross_crate {
442 return EvalResult::Allow;
443 }
444
445 let stability = self.lookup_default_body_stability(def_id);
446 debug!(
447 "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}"
448 );
449
450 match stability {
451 Some(DefaultBodyStability {
452 level: hir::StabilityLevel::Unstable { reason, issue, is_soft, .. },
453 feature,
454 }) => {
455 if span.allows_unstable(feature) {
456 debug!("body stability: skipping span={:?} since it is internal", span);
457 return EvalResult::Allow;
458 }
459 if self.features().enabled(feature) {
460 return EvalResult::Allow;
461 }
462
463 EvalResult::Deny {
464 feature,
465 reason: reason.to_opt_reason(),
466 issue,
467 suggestion: None,
468 is_soft,
469 }
470 }
471 Some(_) => {
472 EvalResult::Allow
474 }
475 None => EvalResult::Unmarked,
476 }
477 }
478
479 pub fn check_stability(
489 self,
490 def_id: DefId,
491 id: Option<HirId>,
492 span: Span,
493 method_span: Option<Span>,
494 ) -> bool {
495 self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
496 }
497
498 pub fn check_stability_allow_unstable(
510 self,
511 def_id: DefId,
512 id: Option<HirId>,
513 span: Span,
514 method_span: Option<Span>,
515 allow_unstable: AllowUnstable,
516 ) -> bool {
517 self.check_optional_stability(
518 def_id,
519 id,
520 span,
521 method_span,
522 allow_unstable,
523 |span, def_id| {
524 self.dcx().span_delayed_bug(span, format!("encountered unmarked API: {def_id:?}"));
527 },
528 )
529 }
530
531 pub fn check_optional_stability(
538 self,
539 def_id: DefId,
540 id: Option<HirId>,
541 span: Span,
542 method_span: Option<Span>,
543 allow_unstable: AllowUnstable,
544 unmarked: impl FnOnce(Span, DefId),
545 ) -> bool {
546 let soft_handler = |lint, span, msg: String| {
547 self.node_span_lint(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
548 lint.primary_message(msg);
549 })
550 };
551 let eval_result =
552 self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable);
553 let is_allowed = matches!(eval_result, EvalResult::Allow);
554 match eval_result {
555 EvalResult::Allow => {}
556 EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
557 self.sess,
558 feature,
559 reason,
560 issue,
561 suggestion,
562 is_soft,
563 span,
564 soft_handler,
565 UnstableKind::Regular,
566 ),
567 EvalResult::Unmarked => unmarked(span, def_id),
568 }
569
570 is_allowed
571 }
572
573 pub fn check_const_stability(self, def_id: DefId, span: Span, const_kw_span: Span) {
581 let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
582 if !is_staged_api {
583 return;
584 }
585
586 let cross_crate = !def_id.is_local();
588 if !cross_crate {
589 return;
590 }
591
592 let stability = self.lookup_const_stability(def_id);
593 debug!(
594 "stability: \
595 inspecting def_id={:?} span={:?} of stability={:?}",
596 def_id, span, stability
597 );
598
599 match stability {
600 Some(ConstStability {
601 level: hir::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
602 feature,
603 ..
604 }) => {
605 assert!(!is_soft);
606
607 if span.allows_unstable(feature) {
608 debug!("body stability: skipping span={:?} since it is internal", span);
609 return;
610 }
611 if self.features().enabled(feature) {
612 return;
613 }
614
615 if let Some(implied_by) = implied_by
619 && self.features().enabled(implied_by)
620 {
621 return;
622 }
623
624 report_unstable(
625 self.sess,
626 feature,
627 reason.to_opt_reason(),
628 issue,
629 None,
630 false,
631 span,
632 |_, _, _| {},
633 UnstableKind::Const(const_kw_span),
634 );
635 }
636 Some(_) | None => {}
637 }
638 }
639
640 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
641 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
642 }
643}