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