]>
Commit | Line | Data |
---|---|---|
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 |
4 | pub use self::StabilityLevel::*; |
5 | ||
3c0e092e | 6 | use crate::ty::{self, DefIdTree, TyCtxt}; |
fc512014 | 7 | use rustc_ast::NodeId; |
f2b60f7d | 8 | use rustc_attr::{self as attr, ConstStability, DefaultBodyStability, Deprecation, Stability}; |
5e7ed085 FG |
9 | use rustc_data_structures::fx::FxHashMap; |
10 | use rustc_errors::{Applicability, Diagnostic}; | |
dfeec247 | 11 | use rustc_feature::GateIssue; |
dfeec247 | 12 | use rustc_hir::def::DefKind; |
04454e1e | 13 | use rustc_hir::def_id::{DefId, LocalDefId}; |
923072b8 | 14 | use rustc_hir::{self as hir, HirId}; |
1b1a35ee | 15 | use rustc_middle::ty::print::with_no_trimmed_paths; |
74b04a01 | 16 | use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; |
c295e0f8 | 17 | use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer}; |
74b04a01 | 18 | use rustc_session::parse::feature_err_issue; |
5e7ed085 | 19 | use rustc_session::Session; |
dfeec247 | 20 | use rustc_span::symbol::{sym, Symbol}; |
5e7ed085 | 21 | use rustc_span::Span; |
60c5eb7d XL |
22 | use std::num::NonZeroU32; |
23 | ||
e74abb32 | 24 | #[derive(PartialEq, Clone, Copy, Debug)] |
b039eaaf SL |
25 | pub enum StabilityLevel { |
26 | Unstable, | |
27 | Stable, | |
28 | } | |
29 | ||
5bcae85e | 30 | /// An entry in the `depr_map`. |
923072b8 | 31 | #[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)] |
5bcae85e SL |
32 | pub struct DeprecationEntry { |
33 | /// The metadata of the attribute associated with this entry. | |
34 | pub attr: Deprecation, | |
9fa01778 | 35 | /// The `DefId` where the attr was originally attached. `None` for non-local |
5bcae85e | 36 | /// `DefId`'s. |
94222f64 | 37 | origin: Option<LocalDefId>, |
5bcae85e SL |
38 | } |
39 | ||
40 | impl DeprecationEntry { | |
94222f64 XL |
41 | pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry { |
42 | DeprecationEntry { attr, origin: Some(def_id) } | |
5bcae85e SL |
43 | } |
44 | ||
ea8adc8c | 45 | pub fn external(attr: Deprecation) -> DeprecationEntry { |
dfeec247 | 46 | DeprecationEntry { attr, origin: None } |
5bcae85e SL |
47 | } |
48 | ||
49 | pub fn same_origin(&self, other: &DeprecationEntry) -> bool { | |
50 | match (self.origin, other.origin) { | |
51 | (Some(o1), Some(o2)) => o1 == o2, | |
dfeec247 | 52 | _ => false, |
5bcae85e SL |
53 | } |
54 | } | |
55 | } | |
56 | ||
1a4d82fc | 57 | /// A stability index, giving the stability level for items and methods. |
5869c6ff | 58 | #[derive(HashStable, Debug)] |
5e7ed085 | 59 | pub struct Index { |
62682a34 SL |
60 | /// This is mostly a cache, except the stabilities of local items |
61 | /// are filled by the annotator. | |
5e7ed085 FG |
62 | pub stab_map: FxHashMap<LocalDefId, Stability>, |
63 | pub const_stab_map: FxHashMap<LocalDefId, ConstStability>, | |
f2b60f7d | 64 | pub default_body_stab_map: FxHashMap<LocalDefId, DefaultBodyStability>, |
94222f64 | 65 | pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>, |
064997fb FG |
66 | /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` |
67 | /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute | |
68 | /// exists, then this map will have a `impliee -> implier` entry. | |
69 | /// | |
70 | /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should | |
71 | /// specify their implications (both `implies` and `implied_by`). If only one of the two | |
72 | /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this | |
73 | /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is | |
74 | /// reported, only the `#[stable]` attribute information is available, so the map is necessary | |
75 | /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` | |
76 | /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of | |
77 | /// unstable feature" error for a feature that was implied. | |
78 | pub implications: FxHashMap<Symbol, Symbol>, | |
476ff2be SL |
79 | } |
80 | ||
5e7ed085 FG |
81 | impl Index { |
82 | pub fn local_stability(&self, def_id: LocalDefId) -> Option<Stability> { | |
94222f64 | 83 | self.stab_map.get(&def_id).copied() |
85aaf69f SL |
84 | } |
85 | ||
5e7ed085 | 86 | pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<ConstStability> { |
94222f64 | 87 | self.const_stab_map.get(&def_id).copied() |
60c5eb7d XL |
88 | } |
89 | ||
f2b60f7d FG |
90 | pub fn local_default_body_stability(&self, def_id: LocalDefId) -> Option<DefaultBodyStability> { |
91 | self.default_body_stab_map.get(&def_id).copied() | |
92 | } | |
93 | ||
94222f64 XL |
94 | pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> { |
95 | self.depr_map.get(&def_id).cloned() | |
85aaf69f SL |
96 | } |
97 | } | |
98 | ||
416331ca | 99 | pub fn report_unstable( |
e74abb32 XL |
100 | sess: &Session, |
101 | feature: Symbol, | |
102 | reason: Option<Symbol>, | |
60c5eb7d | 103 | issue: Option<NonZeroU32>, |
3c0e092e | 104 | suggestion: Option<(Span, String, String, Applicability)>, |
e74abb32 XL |
105 | is_soft: bool, |
106 | span: Span, | |
74b04a01 | 107 | soft_handler: impl FnOnce(&'static Lint, Span, &str), |
416331ca XL |
108 | ) { |
109 | let msg = match reason { | |
110 | Some(r) => format!("use of unstable library feature '{}': {}", feature, r), | |
dfeec247 | 111 | None => format!("use of unstable library feature '{}'", &feature), |
416331ca XL |
112 | }; |
113 | ||
5e7ed085 FG |
114 | if is_soft { |
115 | soft_handler(SOFT_UNSTABLE, span, &msg) | |
116 | } else { | |
117 | let mut err = | |
118 | feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg); | |
119 | if let Some((inner_types, ref msg, sugg, applicability)) = suggestion { | |
120 | err.span_suggestion(inner_types, msg, sugg, applicability); | |
416331ca | 121 | } |
5e7ed085 | 122 | err.emit(); |
416331ca XL |
123 | } |
124 | } | |
125 | ||
9fa01778 | 126 | /// Checks whether an item marked with `deprecated(since="X")` is currently |
0731742a | 127 | /// deprecated (i.e., whether X is not greater than the current rustc version). |
c295e0f8 XL |
128 | pub fn deprecation_in_effect(depr: &Deprecation) -> bool { |
129 | let is_since_rustc_version = depr.is_since_rustc_version; | |
a2a8927a | 130 | let since = depr.since.as_ref().map(Symbol::as_str); |
c295e0f8 | 131 | |
83c7162d | 132 | fn parse_version(ver: &str) -> Vec<u32> { |
0731742a | 133 | // We ignore non-integer components of the version (e.g., "nightly"). |
83c7162d XL |
134 | ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() |
135 | } | |
136 | ||
fc512014 | 137 | if !is_since_rustc_version { |
04454e1e | 138 | // The `since` field doesn't have semantic purpose without `#![staged_api]`. |
fc512014 | 139 | return true; |
83c7162d | 140 | } |
fc512014 XL |
141 | |
142 | if let Some(since) = since { | |
143 | if since == "TBD" { | |
144 | return false; | |
145 | } | |
146 | ||
147 | if let Some(rustc) = option_env!("CFG_RELEASE") { | |
148 | let since: Vec<u32> = parse_version(&since); | |
149 | let rustc: Vec<u32> = parse_version(rustc); | |
150 | // We simply treat invalid `since` attributes as relating to a previous | |
151 | // Rust version, thus always displaying the warning. | |
152 | if since.len() != 3 { | |
153 | return true; | |
154 | } | |
155 | return since <= rustc; | |
156 | } | |
157 | }; | |
158 | ||
159 | // Assume deprecation is in effect if "since" field is missing | |
160 | // or if we can't determine the current Rust version. | |
161 | true | |
83c7162d XL |
162 | } |
163 | ||
416331ca | 164 | pub fn deprecation_suggestion( |
5e7ed085 | 165 | diag: &mut Diagnostic, |
3dfed10e | 166 | kind: &str, |
dfeec247 XL |
167 | suggestion: Option<Symbol>, |
168 | span: Span, | |
416331ca XL |
169 | ) { |
170 | if let Some(suggestion) = suggestion { | |
064997fb | 171 | diag.span_suggestion_verbose( |
416331ca | 172 | span, |
3dfed10e | 173 | &format!("replace the use of the deprecated {}", kind), |
923072b8 | 174 | suggestion, |
416331ca XL |
175 | Applicability::MachineApplicable, |
176 | ); | |
177 | } | |
178 | } | |
179 | ||
c295e0f8 XL |
180 | fn deprecation_lint(is_in_effect: bool) -> &'static Lint { |
181 | if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE } | |
182 | } | |
183 | ||
184 | fn deprecation_message( | |
185 | is_in_effect: bool, | |
186 | since: Option<Symbol>, | |
187 | note: Option<Symbol>, | |
188 | kind: &str, | |
189 | path: &str, | |
190 | ) -> String { | |
191 | let message = if is_in_effect { | |
192 | format!("use of deprecated {} `{}`", kind, path) | |
416331ca | 193 | } else { |
a2a8927a | 194 | let since = since.as_ref().map(Symbol::as_str); |
c295e0f8 | 195 | |
5099ac24 | 196 | if since == Some("TBD") { |
c295e0f8 XL |
197 | format!("use of {} `{}` that will be deprecated in a future Rust version", kind, path) |
198 | } else { | |
199 | format!( | |
200 | "use of {} `{}` that will be deprecated in future version {}", | |
201 | kind, | |
202 | path, | |
203 | since.unwrap() | |
204 | ) | |
205 | } | |
416331ca | 206 | }; |
c295e0f8 XL |
207 | |
208 | match note { | |
3dfed10e XL |
209 | Some(reason) => format!("{}: {}", message, reason), |
210 | None => message, | |
c295e0f8 XL |
211 | } |
212 | } | |
213 | ||
214 | pub fn deprecation_message_and_lint( | |
215 | depr: &Deprecation, | |
216 | kind: &str, | |
217 | path: &str, | |
218 | ) -> (String, &'static Lint) { | |
219 | let is_in_effect = deprecation_in_effect(depr); | |
220 | ( | |
221 | deprecation_message(is_in_effect, depr.since, depr.note, kind, path), | |
222 | deprecation_lint(is_in_effect), | |
223 | ) | |
416331ca XL |
224 | } |
225 | ||
9c376795 FG |
226 | pub fn early_report_deprecation( |
227 | lint_buffer: &mut LintBuffer, | |
416331ca XL |
228 | message: &str, |
229 | suggestion: Option<Symbol>, | |
230 | lint: &'static Lint, | |
231 | span: Span, | |
fc512014 | 232 | node_id: NodeId, |
416331ca | 233 | ) { |
dfeec247 | 234 | if span.in_derive_expansion() { |
416331ca XL |
235 | return; |
236 | } | |
237 | ||
238 | let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); | |
fc512014 | 239 | lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag); |
416331ca XL |
240 | } |
241 | ||
242 | fn late_report_deprecation( | |
243 | tcx: TyCtxt<'_>, | |
244 | message: &str, | |
245 | suggestion: Option<Symbol>, | |
246 | lint: &'static Lint, | |
247 | span: Span, | |
136023e0 | 248 | method_span: Option<Span>, |
416331ca | 249 | hir_id: HirId, |
3dfed10e | 250 | def_id: DefId, |
416331ca | 251 | ) { |
dfeec247 | 252 | if span.in_derive_expansion() { |
416331ca XL |
253 | return; |
254 | } | |
136023e0 | 255 | let method_span = method_span.unwrap_or(span); |
2b03887a | 256 | tcx.struct_span_lint_hir(lint, hir_id, method_span, message, |diag| { |
74b04a01 | 257 | if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { |
9ffffee4 | 258 | let kind = tcx.def_descr(def_id); |
2b03887a | 259 | deprecation_suggestion(diag, kind, suggestion, method_span); |
74b04a01 | 260 | } |
2b03887a | 261 | diag |
74b04a01 | 262 | }); |
416331ca XL |
263 | } |
264 | ||
0531ce1d XL |
265 | /// Result of `TyCtxt::eval_stability`. |
266 | pub enum EvalResult { | |
267 | /// We can use the item because it is stable or we provided the | |
268 | /// corresponding feature gate. | |
269 | Allow, | |
270 | /// We cannot use the item because it is unstable and we did not provide the | |
271 | /// corresponding feature gate. | |
3c0e092e XL |
272 | Deny { |
273 | feature: Symbol, | |
274 | reason: Option<Symbol>, | |
275 | issue: Option<NonZeroU32>, | |
276 | suggestion: Option<(Span, String, String, Applicability)>, | |
277 | is_soft: bool, | |
278 | }, | |
0531ce1d XL |
279 | /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. |
280 | Unmarked, | |
281 | } | |
282 | ||
dfeec247 | 283 | // See issue #38412. |
29967ef6 XL |
284 | fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool { |
285 | if tcx.def_kind(def_id) == DefKind::TyParam { | |
286 | // Have no visibility, considered public for the purpose of this check. | |
287 | return false; | |
dfeec247 | 288 | } |
29967ef6 | 289 | match tcx.visibility(def_id) { |
dfeec247 XL |
290 | // Must check stability for `pub` items. |
291 | ty::Visibility::Public => false, | |
476ff2be | 292 | |
dfeec247 XL |
293 | // These are not visible outside crate; therefore |
294 | // stability markers are irrelevant, if even present. | |
f2b60f7d | 295 | ty::Visibility::Restricted(..) => true, |
476ff2be | 296 | } |
dfeec247 | 297 | } |
476ff2be | 298 | |
3c0e092e XL |
299 | // See issue #83250. |
300 | fn suggestion_for_allocator_api( | |
301 | tcx: TyCtxt<'_>, | |
302 | def_id: DefId, | |
303 | span: Span, | |
304 | feature: Symbol, | |
305 | ) -> Option<(Span, String, String, Applicability)> { | |
306 | if feature == sym::allocator_api { | |
04454e1e | 307 | if let Some(trait_) = tcx.opt_parent(def_id) { |
3c0e092e XL |
308 | if tcx.is_diagnostic_item(sym::Vec, trait_) { |
309 | let sm = tcx.sess.parse_sess.source_map(); | |
310 | let inner_types = sm.span_extend_to_prev_char(span, '<', true); | |
311 | if let Ok(snippet) = sm.span_to_snippet(inner_types) { | |
312 | return Some(( | |
313 | inner_types, | |
314 | "consider wrapping the inner types in tuple".to_string(), | |
315 | format!("({})", snippet), | |
316 | Applicability::MaybeIncorrect, | |
317 | )); | |
318 | } | |
319 | } | |
320 | } | |
321 | } | |
322 | None | |
323 | } | |
324 | ||
923072b8 FG |
325 | /// An override option for eval_stability. |
326 | pub enum AllowUnstable { | |
327 | /// Don't emit an unstable error for the item | |
328 | Yes, | |
329 | /// Handle the item normally | |
330 | No, | |
331 | } | |
332 | ||
dfeec247 | 333 | impl<'tcx> TyCtxt<'tcx> { |
0531ce1d XL |
334 | /// Evaluates the stability of an item. |
335 | /// | |
336 | /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding | |
337 | /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending | |
338 | /// unstable feature otherwise. | |
339 | /// | |
340 | /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been | |
341 | /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to | |
342 | /// `id`. | |
17df50a5 XL |
343 | pub fn eval_stability( |
344 | self, | |
345 | def_id: DefId, | |
346 | id: Option<HirId>, | |
347 | span: Span, | |
348 | method_span: Option<Span>, | |
923072b8 FG |
349 | ) -> EvalResult { |
350 | self.eval_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No) | |
351 | } | |
352 | ||
353 | /// Evaluates the stability of an item. | |
354 | /// | |
355 | /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding | |
356 | /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending | |
357 | /// unstable feature otherwise. | |
358 | /// | |
359 | /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been | |
360 | /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to | |
361 | /// `id`. | |
362 | /// | |
363 | /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally. | |
364 | pub fn eval_stability_allow_unstable( | |
365 | self, | |
366 | def_id: DefId, | |
367 | id: Option<HirId>, | |
368 | span: Span, | |
369 | method_span: Option<Span>, | |
370 | allow_unstable: AllowUnstable, | |
17df50a5 | 371 | ) -> EvalResult { |
476ff2be | 372 | // Deprecated attributes apply in-crate and cross-crate. |
0531ce1d XL |
373 | if let Some(id) = id { |
374 | if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { | |
5099ac24 | 375 | let parent_def_id = self.hir().get_parent_item(id); |
dfeec247 | 376 | let skip = self |
f9f354fc | 377 | .lookup_deprecation_entry(parent_def_id.to_def_id()) |
dfeec247 | 378 | .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); |
0731742a | 379 | |
3dfed10e XL |
380 | // #[deprecated] doesn't emit a notice if we're not on the |
381 | // topmost deprecation. For example, if a struct is deprecated, | |
382 | // the use of a field won't be linted. | |
383 | // | |
04454e1e | 384 | // With #![staged_api], we want to emit down the whole |
3dfed10e | 385 | // hierarchy. |
c295e0f8 XL |
386 | let depr_attr = &depr_entry.attr; |
387 | if !skip || depr_attr.is_since_rustc_version { | |
388 | // Calculating message for lint involves calling `self.def_path_str`. | |
389 | // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. | |
390 | // So we skip message calculation altogether, if lint is allowed. | |
391 | let is_in_effect = deprecation_in_effect(depr_attr); | |
392 | let lint = deprecation_lint(is_in_effect); | |
393 | if self.lint_level_at_node(lint, id).0 != Level::Allow { | |
5e7ed085 | 394 | let def_path = with_no_trimmed_paths!(self.def_path_str(def_id)); |
9ffffee4 | 395 | let def_kind = self.def_descr(def_id); |
c295e0f8 XL |
396 | |
397 | late_report_deprecation( | |
398 | self, | |
399 | &deprecation_message( | |
400 | is_in_effect, | |
401 | depr_attr.since, | |
402 | depr_attr.note, | |
403 | def_kind, | |
5e7ed085 | 404 | &def_path, |
c295e0f8 XL |
405 | ), |
406 | depr_attr.suggestion, | |
407 | lint, | |
408 | span, | |
409 | method_span, | |
410 | id, | |
411 | def_id, | |
412 | ); | |
413 | } | |
0531ce1d | 414 | } |
476ff2be | 415 | }; |
476ff2be SL |
416 | } |
417 | ||
04454e1e | 418 | let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some(); |
476ff2be | 419 | if !is_staged_api { |
0531ce1d | 420 | return EvalResult::Allow; |
9cc50fc6 | 421 | } |
476ff2be | 422 | |
f2b60f7d FG |
423 | // Only the cross-crate scenario matters when checking unstable APIs |
424 | let cross_crate = !def_id.is_local(); | |
425 | if !cross_crate { | |
426 | return EvalResult::Allow; | |
427 | } | |
428 | ||
476ff2be | 429 | let stability = self.lookup_stability(def_id); |
dfeec247 XL |
430 | debug!( |
431 | "stability: \ | |
432 | inspecting def_id={:?} span={:?} of stability={:?}", | |
433 | def_id, span, stability | |
434 | ); | |
476ff2be | 435 | |
0731742a | 436 | // Issue #38412: private items lack stability markers. |
dfeec247 | 437 | if skip_stability_check_due_to_privacy(self, def_id) { |
0531ce1d | 438 | return EvalResult::Allow; |
476ff2be | 439 | } |
85aaf69f | 440 | |
476ff2be | 441 | match stability { |
5e7ed085 | 442 | Some(Stability { |
064997fb FG |
443 | level: attr::Unstable { reason, issue, is_soft, implied_by }, |
444 | feature, | |
445 | .. | |
416331ca | 446 | }) => { |
48663c56 | 447 | if span.allows_unstable(feature) { |
9fa01778 XL |
448 | debug!("stability: skipping span={:?} since it is internal", span); |
449 | return EvalResult::Allow; | |
450 | } | |
5e7ed085 | 451 | if self.features().active(feature) { |
0531ce1d | 452 | return EvalResult::Allow; |
85aaf69f | 453 | } |
7cac9316 | 454 | |
064997fb FG |
455 | // If this item was previously part of a now-stabilized feature which is still |
456 | // active (i.e. the user hasn't removed the attribute for the stabilized feature | |
457 | // yet) then allow use of this item. | |
458 | if let Some(implied_by) = implied_by && self.features().active(implied_by) { | |
459 | return EvalResult::Allow; | |
460 | } | |
461 | ||
7cac9316 XL |
462 | // When we're compiling the compiler itself we may pull in |
463 | // crates from crates.io, but those crates may depend on other | |
464 | // crates also pulled in from crates.io. We want to ideally be | |
465 | // able to compile everything without requiring upstream | |
466 | // modifications, so in the case that this looks like a | |
0731742a | 467 | // `rustc_private` crate (e.g., a compiler crate) and we also have |
7cac9316 XL |
468 | // the `-Z force-unstable-if-unmarked` flag present (we're |
469 | // compiling a compiler crate), then let this missing feature | |
470 | // annotation slide. | |
60c5eb7d | 471 | if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { |
064997fb | 472 | if self.sess.opts.unstable_opts.force_unstable_if_unmarked { |
0531ce1d | 473 | return EvalResult::Allow; |
7cac9316 XL |
474 | } |
475 | } | |
476 | ||
923072b8 FG |
477 | if matches!(allow_unstable, AllowUnstable::Yes) { |
478 | return EvalResult::Allow; | |
479 | } | |
480 | ||
3c0e092e | 481 | let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); |
064997fb FG |
482 | EvalResult::Deny { |
483 | feature, | |
484 | reason: reason.to_opt_reason(), | |
485 | issue, | |
486 | suggestion, | |
487 | is_soft, | |
488 | } | |
0531ce1d XL |
489 | } |
490 | Some(_) => { | |
491 | // Stable APIs are always ok to call and deprecated APIs are | |
492 | // handled by the lint emitting logic above. | |
493 | EvalResult::Allow | |
494 | } | |
dfeec247 | 495 | None => EvalResult::Unmarked, |
0531ce1d XL |
496 | } |
497 | } | |
498 | ||
f2b60f7d FG |
499 | /// Evaluates the default-impl stability of an item. |
500 | /// | |
501 | /// Returns `EvalResult::Allow` if the item's default implementation is stable, or unstable but the corresponding | |
502 | /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending | |
503 | /// unstable feature otherwise. | |
504 | pub fn eval_default_body_stability(self, def_id: DefId, span: Span) -> EvalResult { | |
505 | let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some(); | |
506 | if !is_staged_api { | |
507 | return EvalResult::Allow; | |
508 | } | |
509 | ||
510 | // Only the cross-crate scenario matters when checking unstable APIs | |
511 | let cross_crate = !def_id.is_local(); | |
512 | if !cross_crate { | |
513 | return EvalResult::Allow; | |
514 | } | |
515 | ||
516 | let stability = self.lookup_default_body_stability(def_id); | |
517 | debug!( | |
518 | "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}" | |
519 | ); | |
520 | ||
521 | // Issue #38412: private items lack stability markers. | |
522 | if skip_stability_check_due_to_privacy(self, def_id) { | |
523 | return EvalResult::Allow; | |
524 | } | |
525 | ||
526 | match stability { | |
527 | Some(DefaultBodyStability { | |
528 | level: attr::Unstable { reason, issue, is_soft, .. }, | |
529 | feature, | |
530 | }) => { | |
531 | if span.allows_unstable(feature) { | |
532 | debug!("body stability: skipping span={:?} since it is internal", span); | |
533 | return EvalResult::Allow; | |
534 | } | |
535 | if self.features().active(feature) { | |
536 | return EvalResult::Allow; | |
537 | } | |
538 | ||
539 | EvalResult::Deny { | |
540 | feature, | |
541 | reason: reason.to_opt_reason(), | |
542 | issue, | |
543 | suggestion: None, | |
544 | is_soft, | |
545 | } | |
546 | } | |
547 | Some(_) => { | |
548 | // Stable APIs are always ok to call | |
549 | EvalResult::Allow | |
550 | } | |
551 | None => EvalResult::Unmarked, | |
552 | } | |
553 | } | |
554 | ||
0531ce1d XL |
555 | /// Checks if an item is stable or error out. |
556 | /// | |
557 | /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not | |
558 | /// exist, emits an error. | |
559 | /// | |
1b1a35ee XL |
560 | /// This function will also check if the item is deprecated. |
561 | /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. | |
064997fb FG |
562 | /// |
563 | /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. | |
17df50a5 XL |
564 | pub fn check_stability( |
565 | self, | |
566 | def_id: DefId, | |
567 | id: Option<HirId>, | |
568 | span: Span, | |
569 | method_span: Option<Span>, | |
064997fb | 570 | ) -> bool { |
923072b8 FG |
571 | self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No) |
572 | } | |
573 | ||
574 | /// Checks if an item is stable or error out. | |
575 | /// | |
576 | /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not | |
577 | /// exist, emits an error. | |
578 | /// | |
579 | /// This function will also check if the item is deprecated. | |
580 | /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. | |
581 | /// | |
582 | /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally. | |
064997fb FG |
583 | /// |
584 | /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. | |
923072b8 FG |
585 | pub fn check_stability_allow_unstable( |
586 | self, | |
587 | def_id: DefId, | |
588 | id: Option<HirId>, | |
589 | span: Span, | |
590 | method_span: Option<Span>, | |
591 | allow_unstable: AllowUnstable, | |
064997fb | 592 | ) -> bool { |
923072b8 FG |
593 | self.check_optional_stability( |
594 | def_id, | |
595 | id, | |
596 | span, | |
597 | method_span, | |
598 | allow_unstable, | |
599 | |span, def_id| { | |
600 | // The API could be uncallable for other reasons, for example when a private module | |
601 | // was referenced. | |
602 | self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id)); | |
603 | }, | |
604 | ) | |
1b1a35ee XL |
605 | } |
606 | ||
607 | /// Like `check_stability`, except that we permit items to have custom behaviour for | |
608 | /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary | |
609 | /// for default generic parameters, which only have stability attributes if they were | |
610 | /// added after the type on which they're defined. | |
064997fb FG |
611 | /// |
612 | /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. | |
1b1a35ee XL |
613 | pub fn check_optional_stability( |
614 | self, | |
615 | def_id: DefId, | |
616 | id: Option<HirId>, | |
617 | span: Span, | |
17df50a5 | 618 | method_span: Option<Span>, |
923072b8 | 619 | allow_unstable: AllowUnstable, |
29967ef6 | 620 | unmarked: impl FnOnce(Span, DefId), |
064997fb | 621 | ) -> bool { |
74b04a01 | 622 | let soft_handler = |lint, span, msg: &_| { |
2b03887a | 623 | self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg, |lint| lint) |
74b04a01 | 624 | }; |
064997fb FG |
625 | let eval_result = |
626 | self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable); | |
627 | let is_allowed = matches!(eval_result, EvalResult::Allow); | |
628 | match eval_result { | |
0531ce1d | 629 | EvalResult::Allow => {} |
3c0e092e XL |
630 | EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( |
631 | self.sess, | |
632 | feature, | |
633 | reason, | |
634 | issue, | |
635 | suggestion, | |
636 | is_soft, | |
637 | span, | |
638 | soft_handler, | |
639 | ), | |
1b1a35ee | 640 | EvalResult::Unmarked => unmarked(span, def_id), |
85aaf69f | 641 | } |
064997fb FG |
642 | |
643 | is_allowed | |
85aaf69f | 644 | } |
85aaf69f | 645 | |
a7813a04 | 646 | pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> { |
5bcae85e SL |
647 | self.lookup_deprecation_entry(id).map(|depr| depr.attr) |
648 | } | |
1a4d82fc | 649 | } |