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