]>
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 XL |
11 | use rustc_feature::GateIssue; |
12 | use rustc_hir as hir; | |
13 | use rustc_hir::def::DefKind; | |
5e7ed085 | 14 | use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX}; |
dfeec247 | 15 | use rustc_hir::{self, HirId}; |
1b1a35ee | 16 | use rustc_middle::ty::print::with_no_trimmed_paths; |
74b04a01 | 17 | use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; |
c295e0f8 | 18 | use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer}; |
74b04a01 | 19 | use rustc_session::parse::feature_err_issue; |
5e7ed085 | 20 | use rustc_session::Session; |
dfeec247 | 21 | use rustc_span::symbol::{sym, Symbol}; |
5e7ed085 | 22 | use rustc_span::Span; |
60c5eb7d XL |
23 | use std::num::NonZeroU32; |
24 | ||
e74abb32 | 25 | #[derive(PartialEq, Clone, Copy, Debug)] |
b039eaaf SL |
26 | pub enum StabilityLevel { |
27 | Unstable, | |
28 | Stable, | |
29 | } | |
30 | ||
5bcae85e | 31 | /// An entry in the `depr_map`. |
5099ac24 | 32 | #[derive(Copy, Clone, HashStable, Debug)] |
5bcae85e SL |
33 | pub struct DeprecationEntry { |
34 | /// The metadata of the attribute associated with this entry. | |
35 | pub attr: Deprecation, | |
9fa01778 | 36 | /// The `DefId` where the attr was originally attached. `None` for non-local |
5bcae85e | 37 | /// `DefId`'s. |
94222f64 | 38 | origin: Option<LocalDefId>, |
5bcae85e SL |
39 | } |
40 | ||
41 | impl DeprecationEntry { | |
94222f64 XL |
42 | pub fn local(attr: Deprecation, def_id: LocalDefId) -> DeprecationEntry { |
43 | DeprecationEntry { attr, origin: Some(def_id) } | |
5bcae85e SL |
44 | } |
45 | ||
ea8adc8c | 46 | pub fn external(attr: Deprecation) -> DeprecationEntry { |
dfeec247 | 47 | DeprecationEntry { attr, origin: None } |
5bcae85e SL |
48 | } |
49 | ||
50 | pub fn same_origin(&self, other: &DeprecationEntry) -> bool { | |
51 | match (self.origin, other.origin) { | |
52 | (Some(o1), Some(o2)) => o1 == o2, | |
dfeec247 | 53 | _ => false, |
5bcae85e SL |
54 | } |
55 | } | |
56 | } | |
57 | ||
1a4d82fc | 58 | /// A stability index, giving the stability level for items and methods. |
5869c6ff | 59 | #[derive(HashStable, Debug)] |
5e7ed085 | 60 | pub struct Index { |
62682a34 SL |
61 | /// This is mostly a cache, except the stabilities of local items |
62 | /// are filled by the annotator. | |
5e7ed085 FG |
63 | pub stab_map: FxHashMap<LocalDefId, Stability>, |
64 | pub const_stab_map: FxHashMap<LocalDefId, ConstStability>, | |
94222f64 | 65 | pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>, |
476ff2be SL |
66 | } |
67 | ||
5e7ed085 FG |
68 | impl Index { |
69 | pub fn local_stability(&self, def_id: LocalDefId) -> Option<Stability> { | |
94222f64 | 70 | self.stab_map.get(&def_id).copied() |
85aaf69f SL |
71 | } |
72 | ||
5e7ed085 | 73 | pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<ConstStability> { |
94222f64 | 74 | self.const_stab_map.get(&def_id).copied() |
60c5eb7d XL |
75 | } |
76 | ||
94222f64 XL |
77 | pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> { |
78 | self.depr_map.get(&def_id).cloned() | |
85aaf69f SL |
79 | } |
80 | } | |
81 | ||
416331ca | 82 | pub fn report_unstable( |
e74abb32 XL |
83 | sess: &Session, |
84 | feature: Symbol, | |
85 | reason: Option<Symbol>, | |
60c5eb7d | 86 | issue: Option<NonZeroU32>, |
3c0e092e | 87 | suggestion: Option<(Span, String, String, Applicability)>, |
e74abb32 XL |
88 | is_soft: bool, |
89 | span: Span, | |
74b04a01 | 90 | soft_handler: impl FnOnce(&'static Lint, Span, &str), |
416331ca XL |
91 | ) { |
92 | let msg = match reason { | |
93 | Some(r) => format!("use of unstable library feature '{}': {}", feature, r), | |
dfeec247 | 94 | None => format!("use of unstable library feature '{}'", &feature), |
416331ca XL |
95 | }; |
96 | ||
5e7ed085 FG |
97 | if is_soft { |
98 | soft_handler(SOFT_UNSTABLE, span, &msg) | |
99 | } else { | |
100 | let mut err = | |
101 | feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg); | |
102 | if let Some((inner_types, ref msg, sugg, applicability)) = suggestion { | |
103 | err.span_suggestion(inner_types, msg, sugg, applicability); | |
416331ca | 104 | } |
5e7ed085 | 105 | err.emit(); |
416331ca XL |
106 | } |
107 | } | |
108 | ||
9fa01778 | 109 | /// Checks whether an item marked with `deprecated(since="X")` is currently |
0731742a | 110 | /// deprecated (i.e., whether X is not greater than the current rustc version). |
c295e0f8 XL |
111 | pub fn deprecation_in_effect(depr: &Deprecation) -> bool { |
112 | let is_since_rustc_version = depr.is_since_rustc_version; | |
a2a8927a | 113 | let since = depr.since.as_ref().map(Symbol::as_str); |
c295e0f8 | 114 | |
83c7162d | 115 | fn parse_version(ver: &str) -> Vec<u32> { |
0731742a | 116 | // We ignore non-integer components of the version (e.g., "nightly"). |
83c7162d XL |
117 | ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() |
118 | } | |
119 | ||
fc512014 XL |
120 | if !is_since_rustc_version { |
121 | // The `since` field doesn't have semantic purpose in the stable `deprecated` | |
122 | // attribute, only in `rustc_deprecated`. | |
123 | return true; | |
83c7162d | 124 | } |
fc512014 XL |
125 | |
126 | if let Some(since) = since { | |
127 | if since == "TBD" { | |
128 | return false; | |
129 | } | |
130 | ||
131 | if let Some(rustc) = option_env!("CFG_RELEASE") { | |
132 | let since: Vec<u32> = parse_version(&since); | |
133 | let rustc: Vec<u32> = parse_version(rustc); | |
134 | // We simply treat invalid `since` attributes as relating to a previous | |
135 | // Rust version, thus always displaying the warning. | |
136 | if since.len() != 3 { | |
137 | return true; | |
138 | } | |
139 | return since <= rustc; | |
140 | } | |
141 | }; | |
142 | ||
143 | // Assume deprecation is in effect if "since" field is missing | |
144 | // or if we can't determine the current Rust version. | |
145 | true | |
83c7162d XL |
146 | } |
147 | ||
416331ca | 148 | pub fn deprecation_suggestion( |
5e7ed085 | 149 | diag: &mut Diagnostic, |
3dfed10e | 150 | kind: &str, |
dfeec247 XL |
151 | suggestion: Option<Symbol>, |
152 | span: Span, | |
416331ca XL |
153 | ) { |
154 | if let Some(suggestion) = suggestion { | |
155 | diag.span_suggestion( | |
156 | span, | |
3dfed10e | 157 | &format!("replace the use of the deprecated {}", kind), |
416331ca XL |
158 | suggestion.to_string(), |
159 | Applicability::MachineApplicable, | |
160 | ); | |
161 | } | |
162 | } | |
163 | ||
c295e0f8 XL |
164 | fn deprecation_lint(is_in_effect: bool) -> &'static Lint { |
165 | if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE } | |
166 | } | |
167 | ||
168 | fn deprecation_message( | |
169 | is_in_effect: bool, | |
170 | since: Option<Symbol>, | |
171 | note: Option<Symbol>, | |
172 | kind: &str, | |
173 | path: &str, | |
174 | ) -> String { | |
175 | let message = if is_in_effect { | |
176 | format!("use of deprecated {} `{}`", kind, path) | |
416331ca | 177 | } else { |
a2a8927a | 178 | let since = since.as_ref().map(Symbol::as_str); |
c295e0f8 | 179 | |
5099ac24 | 180 | if since == Some("TBD") { |
c295e0f8 XL |
181 | format!("use of {} `{}` that will be deprecated in a future Rust version", kind, path) |
182 | } else { | |
183 | format!( | |
184 | "use of {} `{}` that will be deprecated in future version {}", | |
185 | kind, | |
186 | path, | |
187 | since.unwrap() | |
188 | ) | |
189 | } | |
416331ca | 190 | }; |
c295e0f8 XL |
191 | |
192 | match note { | |
3dfed10e XL |
193 | Some(reason) => format!("{}: {}", message, reason), |
194 | None => message, | |
c295e0f8 XL |
195 | } |
196 | } | |
197 | ||
198 | pub fn deprecation_message_and_lint( | |
199 | depr: &Deprecation, | |
200 | kind: &str, | |
201 | path: &str, | |
202 | ) -> (String, &'static Lint) { | |
203 | let is_in_effect = deprecation_in_effect(depr); | |
204 | ( | |
205 | deprecation_message(is_in_effect, depr.since, depr.note, kind, path), | |
206 | deprecation_lint(is_in_effect), | |
207 | ) | |
416331ca XL |
208 | } |
209 | ||
a2a8927a | 210 | pub fn early_report_deprecation<'a>( |
dfeec247 | 211 | lint_buffer: &'a mut LintBuffer, |
416331ca XL |
212 | message: &str, |
213 | suggestion: Option<Symbol>, | |
214 | lint: &'static Lint, | |
215 | span: Span, | |
fc512014 | 216 | node_id: NodeId, |
416331ca | 217 | ) { |
dfeec247 | 218 | if span.in_derive_expansion() { |
416331ca XL |
219 | return; |
220 | } | |
221 | ||
222 | let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); | |
fc512014 | 223 | lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag); |
416331ca XL |
224 | } |
225 | ||
226 | fn late_report_deprecation( | |
227 | tcx: TyCtxt<'_>, | |
228 | message: &str, | |
229 | suggestion: Option<Symbol>, | |
230 | lint: &'static Lint, | |
231 | span: Span, | |
136023e0 | 232 | method_span: Option<Span>, |
416331ca | 233 | hir_id: HirId, |
3dfed10e | 234 | def_id: DefId, |
416331ca | 235 | ) { |
dfeec247 | 236 | if span.in_derive_expansion() { |
416331ca XL |
237 | return; |
238 | } | |
136023e0 XL |
239 | let method_span = method_span.unwrap_or(span); |
240 | tcx.struct_span_lint_hir(lint, hir_id, method_span, |lint| { | |
74b04a01 XL |
241 | let mut diag = lint.build(message); |
242 | if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { | |
3dfed10e | 243 | let kind = tcx.def_kind(def_id).descr(def_id); |
136023e0 | 244 | deprecation_suggestion(&mut diag, kind, suggestion, method_span); |
74b04a01 | 245 | } |
5e7ed085 | 246 | diag.emit(); |
74b04a01 | 247 | }); |
416331ca XL |
248 | } |
249 | ||
0531ce1d XL |
250 | /// Result of `TyCtxt::eval_stability`. |
251 | pub enum EvalResult { | |
252 | /// We can use the item because it is stable or we provided the | |
253 | /// corresponding feature gate. | |
254 | Allow, | |
255 | /// We cannot use the item because it is unstable and we did not provide the | |
256 | /// corresponding feature gate. | |
3c0e092e XL |
257 | Deny { |
258 | feature: Symbol, | |
259 | reason: Option<Symbol>, | |
260 | issue: Option<NonZeroU32>, | |
261 | suggestion: Option<(Span, String, String, Applicability)>, | |
262 | is_soft: bool, | |
263 | }, | |
0531ce1d XL |
264 | /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. |
265 | Unmarked, | |
266 | } | |
267 | ||
dfeec247 | 268 | // See issue #38412. |
29967ef6 XL |
269 | fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool { |
270 | if tcx.def_kind(def_id) == DefKind::TyParam { | |
271 | // Have no visibility, considered public for the purpose of this check. | |
272 | return false; | |
dfeec247 | 273 | } |
29967ef6 | 274 | match tcx.visibility(def_id) { |
dfeec247 XL |
275 | // Must check stability for `pub` items. |
276 | ty::Visibility::Public => false, | |
476ff2be | 277 | |
dfeec247 XL |
278 | // These are not visible outside crate; therefore |
279 | // stability markers are irrelevant, if even present. | |
280 | ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true, | |
476ff2be | 281 | } |
dfeec247 | 282 | } |
476ff2be | 283 | |
3c0e092e XL |
284 | // See issue #83250. |
285 | fn suggestion_for_allocator_api( | |
286 | tcx: TyCtxt<'_>, | |
287 | def_id: DefId, | |
288 | span: Span, | |
289 | feature: Symbol, | |
290 | ) -> Option<(Span, String, String, Applicability)> { | |
291 | if feature == sym::allocator_api { | |
292 | if let Some(trait_) = tcx.parent(def_id) { | |
293 | if tcx.is_diagnostic_item(sym::Vec, trait_) { | |
294 | let sm = tcx.sess.parse_sess.source_map(); | |
295 | let inner_types = sm.span_extend_to_prev_char(span, '<', true); | |
296 | if let Ok(snippet) = sm.span_to_snippet(inner_types) { | |
297 | return Some(( | |
298 | inner_types, | |
299 | "consider wrapping the inner types in tuple".to_string(), | |
300 | format!("({})", snippet), | |
301 | Applicability::MaybeIncorrect, | |
302 | )); | |
303 | } | |
304 | } | |
305 | } | |
306 | } | |
307 | None | |
308 | } | |
309 | ||
dfeec247 | 310 | impl<'tcx> TyCtxt<'tcx> { |
0531ce1d XL |
311 | /// Evaluates the stability of an item. |
312 | /// | |
313 | /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding | |
314 | /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending | |
315 | /// unstable feature otherwise. | |
316 | /// | |
317 | /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been | |
318 | /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to | |
319 | /// `id`. | |
17df50a5 XL |
320 | pub fn eval_stability( |
321 | self, | |
322 | def_id: DefId, | |
323 | id: Option<HirId>, | |
324 | span: Span, | |
325 | method_span: Option<Span>, | |
326 | ) -> EvalResult { | |
476ff2be | 327 | // Deprecated attributes apply in-crate and cross-crate. |
0531ce1d XL |
328 | if let Some(id) = id { |
329 | if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { | |
5099ac24 | 330 | let parent_def_id = self.hir().get_parent_item(id); |
dfeec247 | 331 | let skip = self |
f9f354fc | 332 | .lookup_deprecation_entry(parent_def_id.to_def_id()) |
dfeec247 | 333 | .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); |
0731742a | 334 | |
3dfed10e XL |
335 | // #[deprecated] doesn't emit a notice if we're not on the |
336 | // topmost deprecation. For example, if a struct is deprecated, | |
337 | // the use of a field won't be linted. | |
338 | // | |
339 | // #[rustc_deprecated] however wants to emit down the whole | |
340 | // hierarchy. | |
c295e0f8 XL |
341 | let depr_attr = &depr_entry.attr; |
342 | if !skip || depr_attr.is_since_rustc_version { | |
343 | // Calculating message for lint involves calling `self.def_path_str`. | |
344 | // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. | |
345 | // So we skip message calculation altogether, if lint is allowed. | |
346 | let is_in_effect = deprecation_in_effect(depr_attr); | |
347 | let lint = deprecation_lint(is_in_effect); | |
348 | if self.lint_level_at_node(lint, id).0 != Level::Allow { | |
5e7ed085 | 349 | let def_path = with_no_trimmed_paths!(self.def_path_str(def_id)); |
c295e0f8 XL |
350 | let def_kind = self.def_kind(def_id).descr(def_id); |
351 | ||
352 | late_report_deprecation( | |
353 | self, | |
354 | &deprecation_message( | |
355 | is_in_effect, | |
356 | depr_attr.since, | |
357 | depr_attr.note, | |
358 | def_kind, | |
5e7ed085 | 359 | &def_path, |
c295e0f8 XL |
360 | ), |
361 | depr_attr.suggestion, | |
362 | lint, | |
363 | span, | |
364 | method_span, | |
365 | id, | |
366 | def_id, | |
367 | ); | |
368 | } | |
0531ce1d | 369 | } |
476ff2be | 370 | }; |
476ff2be SL |
371 | } |
372 | ||
dfeec247 XL |
373 | let is_staged_api = |
374 | self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some(); | |
476ff2be | 375 | if !is_staged_api { |
0531ce1d | 376 | return EvalResult::Allow; |
9cc50fc6 | 377 | } |
476ff2be SL |
378 | |
379 | let stability = self.lookup_stability(def_id); | |
dfeec247 XL |
380 | debug!( |
381 | "stability: \ | |
382 | inspecting def_id={:?} span={:?} of stability={:?}", | |
383 | def_id, span, stability | |
384 | ); | |
476ff2be | 385 | |
85aaf69f | 386 | // Only the cross-crate scenario matters when checking unstable APIs |
476ff2be | 387 | let cross_crate = !def_id.is_local(); |
b039eaaf | 388 | if !cross_crate { |
0531ce1d | 389 | return EvalResult::Allow; |
b039eaaf SL |
390 | } |
391 | ||
0731742a | 392 | // Issue #38412: private items lack stability markers. |
dfeec247 | 393 | if skip_stability_check_due_to_privacy(self, def_id) { |
0531ce1d | 394 | return EvalResult::Allow; |
476ff2be | 395 | } |
85aaf69f | 396 | |
476ff2be | 397 | match stability { |
5e7ed085 | 398 | Some(Stability { |
416331ca XL |
399 | level: attr::Unstable { reason, issue, is_soft }, feature, .. |
400 | }) => { | |
48663c56 | 401 | if span.allows_unstable(feature) { |
9fa01778 XL |
402 | debug!("stability: skipping span={:?} since it is internal", span); |
403 | return EvalResult::Allow; | |
404 | } | |
5e7ed085 | 405 | if self.features().active(feature) { |
0531ce1d | 406 | return EvalResult::Allow; |
85aaf69f | 407 | } |
7cac9316 XL |
408 | |
409 | // When we're compiling the compiler itself we may pull in | |
410 | // crates from crates.io, but those crates may depend on other | |
411 | // crates also pulled in from crates.io. We want to ideally be | |
412 | // able to compile everything without requiring upstream | |
413 | // modifications, so in the case that this looks like a | |
0731742a | 414 | // `rustc_private` crate (e.g., a compiler crate) and we also have |
7cac9316 XL |
415 | // the `-Z force-unstable-if-unmarked` flag present (we're |
416 | // compiling a compiler crate), then let this missing feature | |
417 | // annotation slide. | |
60c5eb7d | 418 | if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { |
7cac9316 | 419 | if self.sess.opts.debugging_opts.force_unstable_if_unmarked { |
0531ce1d | 420 | return EvalResult::Allow; |
7cac9316 XL |
421 | } |
422 | } | |
423 | ||
3c0e092e XL |
424 | let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); |
425 | EvalResult::Deny { feature, reason, issue, suggestion, is_soft } | |
0531ce1d XL |
426 | } |
427 | Some(_) => { | |
428 | // Stable APIs are always ok to call and deprecated APIs are | |
429 | // handled by the lint emitting logic above. | |
430 | EvalResult::Allow | |
431 | } | |
dfeec247 | 432 | None => EvalResult::Unmarked, |
0531ce1d XL |
433 | } |
434 | } | |
435 | ||
436 | /// Checks if an item is stable or error out. | |
437 | /// | |
438 | /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not | |
439 | /// exist, emits an error. | |
440 | /// | |
1b1a35ee XL |
441 | /// This function will also check if the item is deprecated. |
442 | /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. | |
17df50a5 XL |
443 | pub fn check_stability( |
444 | self, | |
445 | def_id: DefId, | |
446 | id: Option<HirId>, | |
447 | span: Span, | |
448 | method_span: Option<Span>, | |
449 | ) { | |
450 | self.check_optional_stability(def_id, id, span, method_span, |span, def_id| { | |
1b1a35ee XL |
451 | // The API could be uncallable for other reasons, for example when a private module |
452 | // was referenced. | |
453 | self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id)); | |
454 | }) | |
455 | } | |
456 | ||
457 | /// Like `check_stability`, except that we permit items to have custom behaviour for | |
458 | /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary | |
459 | /// for default generic parameters, which only have stability attributes if they were | |
460 | /// added after the type on which they're defined. | |
461 | pub fn check_optional_stability( | |
462 | self, | |
463 | def_id: DefId, | |
464 | id: Option<HirId>, | |
465 | span: Span, | |
17df50a5 | 466 | method_span: Option<Span>, |
29967ef6 | 467 | unmarked: impl FnOnce(Span, DefId), |
1b1a35ee | 468 | ) { |
74b04a01 XL |
469 | let soft_handler = |lint, span, msg: &_| { |
470 | self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { | |
5e7ed085 | 471 | lint.build(msg).emit(); |
74b04a01 XL |
472 | }) |
473 | }; | |
17df50a5 | 474 | match self.eval_stability(def_id, id, span, method_span) { |
0531ce1d | 475 | EvalResult::Allow => {} |
3c0e092e XL |
476 | EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( |
477 | self.sess, | |
478 | feature, | |
479 | reason, | |
480 | issue, | |
481 | suggestion, | |
482 | is_soft, | |
483 | span, | |
484 | soft_handler, | |
485 | ), | |
1b1a35ee | 486 | EvalResult::Unmarked => unmarked(span, def_id), |
85aaf69f SL |
487 | } |
488 | } | |
85aaf69f | 489 | |
a7813a04 | 490 | pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> { |
5bcae85e SL |
491 | self.lookup_deprecation_entry(id).map(|depr| depr.attr) |
492 | } | |
1a4d82fc | 493 | } |