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