1 //! A pass that annotates every item and method with its stability level,
2 //! propagating default levels lexically from parent to children ast nodes.
4 pub use self::StabilityLevel
::*;
6 use crate::ty
::{self, TyCtxt}
;
7 use rustc_ast
::CRATE_NODE_ID
;
8 use rustc_attr
::{self as attr, ConstStability, Deprecation, Stability}
;
9 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
10 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
11 use rustc_feature
::GateIssue
;
13 use rustc_hir
::def
::DefKind
;
14 use rustc_hir
::def_id
::{CrateNum, DefId, CRATE_DEF_INDEX}
;
15 use rustc_hir
::{self, HirId}
;
16 use rustc_middle
::ty
::print
::with_no_trimmed_paths
;
17 use rustc_session
::lint
::builtin
::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}
;
18 use rustc_session
::lint
::{BuiltinLintDiagnostics, Lint, LintBuffer}
;
19 use rustc_session
::parse
::feature_err_issue
;
20 use rustc_session
::{DiagnosticMessageId, Session}
;
21 use rustc_span
::symbol
::{sym, Symbol}
;
22 use rustc_span
::{MultiSpan, Span}
;
24 use std
::num
::NonZeroU32
;
26 #[derive(PartialEq, Clone, Copy, Debug)]
27 pub enum StabilityLevel
{
33 pub fn from_attr_level(level
: &attr
::StabilityLevel
) -> Self {
34 if level
.is_stable() { Stable }
else { Unstable }
38 /// An entry in the `depr_map`.
39 #[derive(Clone, HashStable)]
40 pub struct DeprecationEntry
{
41 /// The metadata of the attribute associated with this entry.
42 pub attr
: Deprecation
,
43 /// The `DefId` where the attr was originally attached. `None` for non-local
45 origin
: Option
<HirId
>,
48 impl DeprecationEntry
{
49 pub fn local(attr
: Deprecation
, id
: HirId
) -> DeprecationEntry
{
50 DeprecationEntry { attr, origin: Some(id) }
53 pub fn external(attr
: Deprecation
) -> DeprecationEntry
{
54 DeprecationEntry { attr, origin: None }
57 pub fn same_origin(&self, other
: &DeprecationEntry
) -> bool
{
58 match (self.origin
, other
.origin
) {
59 (Some(o1
), Some(o2
)) => o1
== o2
,
65 /// A stability index, giving the stability level for items and methods.
67 pub struct Index
<'tcx
> {
68 /// This is mostly a cache, except the stabilities of local items
69 /// are filled by the annotator.
70 pub stab_map
: FxHashMap
<HirId
, &'tcx Stability
>,
71 pub const_stab_map
: FxHashMap
<HirId
, &'tcx ConstStability
>,
72 pub depr_map
: FxHashMap
<HirId
, DeprecationEntry
>,
74 /// Maps for each crate whether it is part of the staged API.
75 pub staged_api
: FxHashMap
<CrateNum
, bool
>,
77 /// Features enabled for this crate.
78 pub active_features
: FxHashSet
<Symbol
>,
81 impl<'tcx
> Index
<'tcx
> {
82 pub fn local_stability(&self, id
: HirId
) -> Option
<&'tcx Stability
> {
83 self.stab_map
.get(&id
).cloned()
86 pub fn local_const_stability(&self, id
: HirId
) -> Option
<&'tcx ConstStability
> {
87 self.const_stab_map
.get(&id
).cloned()
90 pub fn local_deprecation_entry(&self, id
: HirId
) -> Option
<DeprecationEntry
> {
91 self.depr_map
.get(&id
).cloned()
95 pub fn report_unstable(
98 reason
: Option
<Symbol
>,
99 issue
: Option
<NonZeroU32
>,
102 soft_handler
: impl FnOnce(&'
static Lint
, Span
, &str),
104 let msg
= match reason
{
105 Some(r
) => format
!("use of unstable library feature '{}': {}", feature
, r
),
106 None
=> format
!("use of unstable library feature '{}'", &feature
),
109 let msp
: MultiSpan
= span
.into();
110 let sm
= &sess
.parse_sess
.source_map();
111 let span_key
= msp
.primary_span().and_then(|sp
: Span
| {
113 let file
= sm
.lookup_char_pos(sp
.lo()).file
;
114 if file
.is_imported() { None }
else { Some(span) }
120 let error_id
= (DiagnosticMessageId
::StabilityId(issue
), span_key
, msg
.clone());
121 let fresh
= sess
.one_time_diagnostics
.borrow_mut().insert(error_id
);
124 soft_handler(SOFT_UNSTABLE
, span
, &msg
)
126 feature_err_issue(&sess
.parse_sess
, feature
, span
, GateIssue
::Library(issue
), &msg
)
132 /// Checks whether an item marked with `deprecated(since="X")` is currently
133 /// deprecated (i.e., whether X is not greater than the current rustc version).
134 pub fn deprecation_in_effect(is_since_rustc_version
: bool
, since
: Option
<&str>) -> bool
{
135 let since
= if let Some(since
) = since
{
136 if is_since_rustc_version
{
139 // We assume that the deprecation is in effect if it's not a
144 // If since attribute is not set, then we're definitely in effect.
147 fn parse_version(ver
: &str) -> Vec
<u32> {
148 // We ignore non-integer components of the version (e.g., "nightly").
149 ver
.split(|c
| c
== '
.'
|| c
== '
-'
).flat_map(|s
| s
.parse()).collect()
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 {
162 // By default, a deprecation warning applies to
163 // the current version of the compiler.
168 pub fn deprecation_suggestion(
169 diag
: &mut DiagnosticBuilder
<'_
>,
171 suggestion
: Option
<Symbol
>,
174 if let Some(suggestion
) = suggestion
{
175 diag
.span_suggestion(
177 &format
!("replace the use of the deprecated {}", kind
),
178 suggestion
.to_string(),
179 Applicability
::MachineApplicable
,
184 pub fn deprecation_message(depr
: &Deprecation
, kind
: &str, path
: &str) -> (String
, &'
static Lint
) {
185 let (message
, lint
) = if deprecation_in_effect(
186 depr
.is_since_rustc_version
,
187 depr
.since
.map(Symbol
::as_str
).as_deref(),
189 (format
!("use of deprecated {} `{}`", kind
, path
), DEPRECATED
)
193 "use of {} `{}` that will be deprecated in future version {}",
198 DEPRECATED_IN_FUTURE
,
201 let message
= match depr
.note
{
202 Some(reason
) => format
!("{}: {}", message
, reason
),
208 pub fn early_report_deprecation(
209 lint_buffer
: &'a
mut LintBuffer
,
211 suggestion
: Option
<Symbol
>,
215 if span
.in_derive_expansion() {
219 let diag
= BuiltinLintDiagnostics
::DeprecatedMacro(suggestion
, span
);
220 lint_buffer
.buffer_lint_with_diagnostic(lint
, CRATE_NODE_ID
, span
, message
, diag
);
223 fn late_report_deprecation(
226 suggestion
: Option
<Symbol
>,
232 if span
.in_derive_expansion() {
236 tcx
.struct_span_lint_hir(lint
, hir_id
, span
, |lint
| {
237 let mut diag
= lint
.build(message
);
238 if let hir
::Node
::Expr(_
) = tcx
.hir().get(hir_id
) {
239 let kind
= tcx
.def_kind(def_id
).descr(def_id
);
240 deprecation_suggestion(&mut diag
, kind
, suggestion
, span
);
246 /// Result of `TyCtxt::eval_stability`.
247 pub enum EvalResult
{
248 /// We can use the item because it is stable or we provided the
249 /// corresponding feature gate.
251 /// We cannot use the item because it is unstable and we did not provide the
252 /// corresponding feature gate.
253 Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }
,
254 /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
259 fn skip_stability_check_due_to_privacy(tcx
: TyCtxt
<'_
>, mut def_id
: DefId
) -> bool
{
260 // Check if `def_id` is a trait method.
261 match tcx
.def_kind(def_id
) {
262 DefKind
::AssocFn
| DefKind
::AssocTy
| DefKind
::AssocConst
=> {
263 if let ty
::TraitContainer(trait_def_id
) = tcx
.associated_item(def_id
).container
{
264 // Trait methods do not declare visibility (even
265 // for visibility info in cstore). Use containing
266 // trait instead, so methods of `pub` traits are
267 // themselves considered `pub`.
268 def_id
= trait_def_id
;
274 let visibility
= tcx
.visibility(def_id
);
277 // Must check stability for `pub` items.
278 ty
::Visibility
::Public
=> false,
280 // These are not visible outside crate; therefore
281 // stability markers are irrelevant, if even present.
282 ty
::Visibility
::Restricted(..) | ty
::Visibility
::Invisible
=> true,
286 impl<'tcx
> TyCtxt
<'tcx
> {
287 /// Evaluates the stability of an item.
289 /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
290 /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
291 /// unstable feature otherwise.
293 /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
294 /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
296 pub fn eval_stability(self, def_id
: DefId
, id
: Option
<HirId
>, span
: Span
) -> EvalResult
{
297 // Deprecated attributes apply in-crate and cross-crate.
298 if let Some(id
) = id
{
299 if let Some(depr_entry
) = self.lookup_deprecation_entry(def_id
) {
300 let parent_def_id
= self.hir().local_def_id(self.hir().get_parent_item(id
));
302 .lookup_deprecation_entry(parent_def_id
.to_def_id())
303 .map_or(false, |parent_depr
| parent_depr
.same_origin(&depr_entry
));
305 // #[deprecated] doesn't emit a notice if we're not on the
306 // topmost deprecation. For example, if a struct is deprecated,
307 // the use of a field won't be linted.
309 // #[rustc_deprecated] however wants to emit down the whole
311 if !skip
|| depr_entry
.attr
.is_since_rustc_version
{
312 let path
= &with_no_trimmed_paths(|| self.def_path_str(def_id
));
313 let kind
= self.def_kind(def_id
).descr(def_id
);
314 let (message
, lint
) = deprecation_message(&depr_entry
.attr
, kind
, path
);
315 late_report_deprecation(
318 depr_entry
.attr
.suggestion
,
329 self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }
).is_some();
331 return EvalResult
::Allow
;
334 let stability
= self.lookup_stability(def_id
);
337 inspecting def_id={:?} span={:?} of stability={:?}",
338 def_id
, span
, stability
341 // Only the cross-crate scenario matters when checking unstable APIs
342 let cross_crate
= !def_id
.is_local();
344 return EvalResult
::Allow
;
347 // Issue #38412: private items lack stability markers.
348 if skip_stability_check_due_to_privacy(self, def_id
) {
349 return EvalResult
::Allow
;
354 level
: attr
::Unstable { reason, issue, is_soft }
, feature
, ..
356 if span
.allows_unstable(feature
) {
357 debug
!("stability: skipping span={:?} since it is internal", span
);
358 return EvalResult
::Allow
;
360 if self.stability().active_features
.contains(&feature
) {
361 return EvalResult
::Allow
;
364 // When we're compiling the compiler itself we may pull in
365 // crates from crates.io, but those crates may depend on other
366 // crates also pulled in from crates.io. We want to ideally be
367 // able to compile everything without requiring upstream
368 // modifications, so in the case that this looks like a
369 // `rustc_private` crate (e.g., a compiler crate) and we also have
370 // the `-Z force-unstable-if-unmarked` flag present (we're
371 // compiling a compiler crate), then let this missing feature
373 if feature
== sym
::rustc_private
&& issue
== NonZeroU32
::new(27812) {
374 if self.sess
.opts
.debugging_opts
.force_unstable_if_unmarked
{
375 return EvalResult
::Allow
;
379 EvalResult
::Deny { feature, reason, issue, is_soft }
382 // Stable APIs are always ok to call and deprecated APIs are
383 // handled by the lint emitting logic above.
386 None
=> EvalResult
::Unmarked
,
390 /// Checks if an item is stable or error out.
392 /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
393 /// exist, emits an error.
395 /// This function will also check if the item is deprecated.
396 /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
397 pub fn check_stability(self, def_id
: DefId
, id
: Option
<HirId
>, span
: Span
) {
398 self.check_optional_stability(def_id
, id
, span
, |span
, def_id
| {
399 // The API could be uncallable for other reasons, for example when a private module
401 self.sess
.delay_span_bug(span
, &format
!("encountered unmarked API: {:?}", def_id
));
405 /// Like `check_stability`, except that we permit items to have custom behaviour for
406 /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary
407 /// for default generic parameters, which only have stability attributes if they were
408 /// added after the type on which they're defined.
409 pub fn check_optional_stability(
414 unmarked
: impl FnOnce(Span
, DefId
) -> (),
416 let soft_handler
= |lint
, span
, msg
: &_
| {
417 self.struct_span_lint_hir(lint
, id
.unwrap_or(hir
::CRATE_HIR_ID
), span
, |lint
| {
418 lint
.build(msg
).emit()
421 match self.eval_stability(def_id
, id
, span
) {
422 EvalResult
::Allow
=> {}
423 EvalResult
::Deny { feature, reason, issue, is_soft }
=> {
424 report_unstable(self.sess
, feature
, reason
, issue
, is_soft
, span
, soft_handler
)
426 EvalResult
::Unmarked
=> unmarked(span
, def_id
),
430 pub fn lookup_deprecation(self, id
: DefId
) -> Option
<Deprecation
> {
431 self.lookup_deprecation_entry(id
).map(|depr
| depr
.attr
)