]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/levels.rs
Auto merge of #100966 - compiler-errors:revert-remove-deferred-sized-checks, r=pnkfelix
[rust.git] / compiler / rustc_lint / src / levels.rs
1 use crate::context::{CheckLintNameResult, LintStore};
2 use crate::late::unerased_lint_store;
3 use rustc_ast as ast;
4 use rustc_ast_pretty::pprust;
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_errors::{Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan};
7 use rustc_hir as hir;
8 use rustc_hir::{intravisit, HirId};
9 use rustc_middle::hir::nested_filter;
10 use rustc_middle::lint::{
11     struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets,
12     LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE,
13 };
14 use rustc_middle::ty::query::Providers;
15 use rustc_middle::ty::{RegisteredTools, TyCtxt};
16 use rustc_session::lint::{
17     builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
18     Level, Lint, LintExpectationId, LintId,
19 };
20 use rustc_session::parse::{add_feature_diagnostics, feature_err};
21 use rustc_session::Session;
22 use rustc_span::symbol::{sym, Symbol};
23 use rustc_span::{Span, DUMMY_SP};
24
25 use crate::errors::{
26     MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
27     UnknownToolInScopedLint,
28 };
29
30 fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
31     let store = unerased_lint_store(tcx);
32     let levels =
33         LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools);
34     let mut builder = LintLevelMapBuilder { levels, tcx };
35     let krate = tcx.hir().krate();
36
37     builder.levels.id_to_set.reserve(krate.owners.len() + 1);
38
39     let push =
40         builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true, Some(hir::CRATE_HIR_ID));
41
42     builder.levels.register_id(hir::CRATE_HIR_ID);
43     tcx.hir().walk_toplevel_module(&mut builder);
44     builder.levels.pop(push);
45
46     builder.levels.update_unstable_expectation_ids();
47     builder.levels.build_map()
48 }
49
50 pub struct LintLevelsBuilder<'s> {
51     sess: &'s Session,
52     lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
53     /// Each expectation has a stable and an unstable identifier. This map
54     /// is used to map from unstable to stable [`LintExpectationId`]s.
55     expectation_id_map: FxHashMap<LintExpectationId, LintExpectationId>,
56     sets: LintLevelSets,
57     id_to_set: FxHashMap<HirId, LintStackIndex>,
58     cur: LintStackIndex,
59     warn_about_weird_lints: bool,
60     store: &'s LintStore,
61     registered_tools: &'s RegisteredTools,
62 }
63
64 pub struct BuilderPush {
65     prev: LintStackIndex,
66     pub changed: bool,
67 }
68
69 impl<'s> LintLevelsBuilder<'s> {
70     pub fn new(
71         sess: &'s Session,
72         warn_about_weird_lints: bool,
73         store: &'s LintStore,
74         registered_tools: &'s RegisteredTools,
75     ) -> Self {
76         let mut builder = LintLevelsBuilder {
77             sess,
78             lint_expectations: Default::default(),
79             expectation_id_map: Default::default(),
80             sets: LintLevelSets::new(),
81             cur: COMMAND_LINE,
82             id_to_set: Default::default(),
83             warn_about_weird_lints,
84             store,
85             registered_tools,
86         };
87         builder.process_command_line(sess, store);
88         assert_eq!(builder.sets.list.len(), 1);
89         builder
90     }
91
92     pub(crate) fn sess(&self) -> &Session {
93         self.sess
94     }
95
96     pub(crate) fn lint_store(&self) -> &LintStore {
97         self.store
98     }
99
100     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
101         &self.sets.list[self.cur].specs
102     }
103
104     fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
105         &mut self.sets.list[self.cur].specs
106     }
107
108     fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
109         self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
110
111         self.cur =
112             self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
113         for &(ref lint_name, level) in &sess.opts.lint_opts {
114             store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools);
115             let orig_level = level;
116             let lint_flag_val = Symbol::intern(lint_name);
117
118             let Ok(ids) = store.find_lints(&lint_name) else {
119                 // errors handled in check_lint_name_cmdline above
120                 continue
121             };
122             for id in ids {
123                 // ForceWarn and Forbid cannot be overridden
124                 if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
125                     self.current_specs().get(&id)
126                 {
127                     continue;
128                 }
129
130                 if self.check_gated_lint(id, DUMMY_SP) {
131                     let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
132                     self.current_specs_mut().insert(id, (level, src));
133                 }
134             }
135         }
136     }
137
138     /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
139     /// (e.g. if a forbid was already inserted on the same scope), then emits a
140     /// diagnostic with no change to `specs`.
141     fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
142         let (old_level, old_src) =
143             self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess);
144         // Setting to a non-forbid level is an error if the lint previously had
145         // a forbid level. Note that this is not necessarily true even with a
146         // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
147         //
148         // This means that this only errors if we're truly lowering the lint
149         // level from forbid.
150         if level != Level::Forbid {
151             if let Level::Forbid = old_level {
152                 // Backwards compatibility check:
153                 //
154                 // We used to not consider `forbid(lint_group)`
155                 // as preventing `allow(lint)` for some lint `lint` in
156                 // `lint_group`. For now, issue a future-compatibility
157                 // warning for this case.
158                 let id_name = id.lint.name_lower();
159                 let fcw_warning = match old_src {
160                     LintLevelSource::Default => false,
161                     LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol),
162                     LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
163                 };
164                 debug!(
165                     "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
166                     fcw_warning,
167                     self.current_specs(),
168                     old_src,
169                     id_name
170                 );
171
172                 let decorate_diag = |diag: &mut Diagnostic| {
173                     diag.span_label(src.span(), "overruled by previous forbid");
174                     match old_src {
175                         LintLevelSource::Default => {
176                             diag.note(&format!(
177                                 "`forbid` lint level is the default for {}",
178                                 id.to_string()
179                             ));
180                         }
181                         LintLevelSource::Node(_, forbid_source_span, reason) => {
182                             diag.span_label(forbid_source_span, "`forbid` level set here");
183                             if let Some(rationale) = reason {
184                                 diag.note(rationale.as_str());
185                             }
186                         }
187                         LintLevelSource::CommandLine(_, _) => {
188                             diag.note("`forbid` lint level was set on command line");
189                         }
190                     }
191                 };
192                 if !fcw_warning {
193                     self.sess.emit_err(OverruledAttribute {
194                         span: src.span(),
195                         overruled: src.span(),
196                         lint_level: level.as_str().to_string(),
197                         lint_source: src.name(),
198                         sub: match old_src {
199                             LintLevelSource::Default => {
200                                 OverruledAttributeSub::DefaultSource { id: id.to_string() }
201                             }
202                             LintLevelSource::Node(_, forbid_source_span, reason) => {
203                                 OverruledAttributeSub::NodeSource {
204                                     span: forbid_source_span,
205                                     reason,
206                                 }
207                             }
208                             LintLevelSource::CommandLine(_, _) => {
209                                 OverruledAttributeSub::CommandLineSource
210                             }
211                         },
212                     });
213                 } else {
214                     self.struct_lint(
215                         FORBIDDEN_LINT_GROUPS,
216                         Some(src.span().into()),
217                         |diag_builder| {
218                             let mut diag_builder = diag_builder.build(&format!(
219                                 "{}({}) incompatible with previous forbid",
220                                 level.as_str(),
221                                 src.name(),
222                             ));
223                             decorate_diag(&mut diag_builder);
224                             diag_builder.emit();
225                         },
226                     );
227                 }
228
229                 // Retain the forbid lint level, unless we are
230                 // issuing a FCW. In the FCW case, we want to
231                 // respect the new setting.
232                 if !fcw_warning {
233                     return;
234                 }
235             }
236         }
237
238         // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
239         // Handling expectations of this lint would add additional complexity with little to no
240         // benefit. The expect level for this lint will therefore be ignored.
241         if let Level::Expect(_) = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) {
242             return;
243         }
244
245         match (old_level, level) {
246             // If the new level is an expectation store it in `ForceWarn`
247             (Level::ForceWarn(_), Level::Expect(expectation_id)) => self
248                 .current_specs_mut()
249                 .insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)),
250             // Keep `ForceWarn` level but drop the expectation
251             (Level::ForceWarn(_), _) => {
252                 self.current_specs_mut().insert(id, (Level::ForceWarn(None), old_src))
253             }
254             // Set the lint level as normal
255             _ => self.current_specs_mut().insert(id, (level, src)),
256         };
257     }
258
259     /// Pushes a list of AST lint attributes onto this context.
260     ///
261     /// This function will return a `BuilderPush` object which should be passed
262     /// to `pop` when this scope for the attributes provided is exited.
263     ///
264     /// This function will perform a number of tasks:
265     ///
266     /// * It'll validate all lint-related attributes in `attrs`
267     /// * It'll mark all lint-related attributes as used
268     /// * Lint levels will be updated based on the attributes provided
269     /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
270     ///   `#[allow]`
271     ///
272     /// Don't forget to call `pop`!
273     pub(crate) fn push(
274         &mut self,
275         attrs: &[ast::Attribute],
276         is_crate_node: bool,
277         source_hir_id: Option<HirId>,
278     ) -> BuilderPush {
279         let prev = self.cur;
280         self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
281
282         let sess = self.sess;
283         for (attr_index, attr) in attrs.iter().enumerate() {
284             if attr.has_name(sym::automatically_derived) {
285                 self.current_specs_mut().insert(
286                     LintId::of(SINGLE_USE_LIFETIMES),
287                     (Level::Allow, LintLevelSource::Default),
288                 );
289                 continue;
290             }
291
292             let level = match Level::from_attr(attr) {
293                 None => continue,
294                 // This is the only lint level with a `LintExpectationId` that can be created from an attribute
295                 Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
296                     let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index);
297
298                     Level::Expect(stable_id)
299                 }
300                 Some(lvl) => lvl,
301             };
302
303             let Some(mut metas) = attr.meta_item_list() else {
304                 continue
305             };
306
307             if metas.is_empty() {
308                 // This emits the unused_attributes lint for `#[level()]`
309                 continue;
310             }
311
312             // Before processing the lint names, look for a reason (RFC 2383)
313             // at the end.
314             let mut reason = None;
315             let tail_li = &metas[metas.len() - 1];
316             if let Some(item) = tail_li.meta_item() {
317                 match item.kind {
318                     ast::MetaItemKind::Word => {} // actual lint names handled later
319                     ast::MetaItemKind::NameValue(ref name_value) => {
320                         if item.path == sym::reason {
321                             if let ast::LitKind::Str(rationale, _) = name_value.kind {
322                                 if !self.sess.features_untracked().lint_reasons {
323                                     feature_err(
324                                         &self.sess.parse_sess,
325                                         sym::lint_reasons,
326                                         item.span,
327                                         "lint reasons are experimental",
328                                     )
329                                     .emit();
330                                 }
331                                 reason = Some(rationale);
332                             } else {
333                                 sess.emit_err(MalformedAttribute {
334                                     span: name_value.span,
335                                     sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
336                                         name_value.span,
337                                     ),
338                                 });
339                             }
340                             // found reason, reslice meta list to exclude it
341                             metas.pop().unwrap();
342                         } else {
343                             sess.emit_err(MalformedAttribute {
344                                 span: item.span,
345                                 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
346                             });
347                         }
348                     }
349                     ast::MetaItemKind::List(_) => {
350                         sess.emit_err(MalformedAttribute {
351                             span: item.span,
352                             sub: MalformedAttributeSub::BadAttributeArgument(item.span),
353                         });
354                     }
355                 }
356             }
357
358             for (lint_index, li) in metas.iter_mut().enumerate() {
359                 let level = match level {
360                     Level::Expect(mut id) => {
361                         id.set_lint_index(Some(lint_index as u16));
362                         Level::Expect(id)
363                     }
364                     level => level,
365                 };
366
367                 let sp = li.span();
368                 let meta_item = match li {
369                     ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
370                     _ => {
371                         if let Some(item) = li.meta_item() {
372                             if let ast::MetaItemKind::NameValue(_) = item.kind {
373                                 if item.path == sym::reason {
374                                     sess.emit_err(MalformedAttribute {
375                                         span: sp,
376                                         sub: MalformedAttributeSub::ReasonMustComeLast(sp),
377                                     });
378                                     continue;
379                                 }
380                             }
381                         }
382                         sess.emit_err(MalformedAttribute {
383                             span: sp,
384                             sub: MalformedAttributeSub::BadAttributeArgument(sp),
385                         });
386                         continue;
387                     }
388                 };
389                 let tool_ident = if meta_item.path.segments.len() > 1 {
390                     Some(meta_item.path.segments.remove(0).ident)
391                 } else {
392                     None
393                 };
394                 let tool_name = tool_ident.map(|ident| ident.name);
395                 let name = pprust::path_to_string(&meta_item.path);
396                 let lint_result =
397                     self.store.check_lint_name(&name, tool_name, self.registered_tools);
398                 match &lint_result {
399                     CheckLintNameResult::Ok(ids) => {
400                         // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]`
401                         // in that case we want to avoid overriding the lint level but instead add an expectation that
402                         // can't be fulfilled. The lint message will include an explanation, that the
403                         // `unfulfilled_lint_expectations` lint can't be expected.
404                         if let Level::Expect(expect_id) = level {
405                             // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we
406                             // only need to check the slice if it contains a single lint.
407                             let is_unfulfilled_lint_expectations = match ids {
408                                 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
409                                 _ => false,
410                             };
411                             self.lint_expectations.push((
412                                 expect_id,
413                                 LintExpectation::new(
414                                     reason,
415                                     sp,
416                                     is_unfulfilled_lint_expectations,
417                                     tool_name,
418                                 ),
419                             ));
420                         }
421                         let src = LintLevelSource::Node(
422                             meta_item.path.segments.last().expect("empty lint name").ident.name,
423                             sp,
424                             reason,
425                         );
426                         for &id in *ids {
427                             if self.check_gated_lint(id, attr.span) {
428                                 self.insert_spec(id, (level, src));
429                             }
430                         }
431                     }
432
433                     CheckLintNameResult::Tool(result) => {
434                         match *result {
435                             Ok(ids) => {
436                                 let complete_name =
437                                     &format!("{}::{}", tool_ident.unwrap().name, name);
438                                 let src = LintLevelSource::Node(
439                                     Symbol::intern(complete_name),
440                                     sp,
441                                     reason,
442                                 );
443                                 for id in ids {
444                                     self.insert_spec(*id, (level, src));
445                                 }
446                                 if let Level::Expect(expect_id) = level {
447                                     self.lint_expectations.push((
448                                         expect_id,
449                                         LintExpectation::new(reason, sp, false, tool_name),
450                                     ));
451                                 }
452                             }
453                             Err((Some(ids), ref new_lint_name)) => {
454                                 let lint = builtin::RENAMED_AND_REMOVED_LINTS;
455                                 let (lvl, src) = self.sets.get_lint_level(
456                                     lint,
457                                     self.cur,
458                                     Some(self.current_specs()),
459                                     &sess,
460                                 );
461                                 struct_lint_level(
462                                     self.sess,
463                                     lint,
464                                     lvl,
465                                     src,
466                                     Some(sp.into()),
467                                     |lint| {
468                                         let msg = format!(
469                                             "lint name `{}` is deprecated \
470                                              and may not have an effect in the future.",
471                                             name
472                                         );
473                                         lint.build(&msg)
474                                             .span_suggestion(
475                                                 sp,
476                                                 "change it to",
477                                                 new_lint_name,
478                                                 Applicability::MachineApplicable,
479                                             )
480                                             .emit();
481                                     },
482                                 );
483
484                                 let src = LintLevelSource::Node(
485                                     Symbol::intern(&new_lint_name),
486                                     sp,
487                                     reason,
488                                 );
489                                 for id in ids {
490                                     self.insert_spec(*id, (level, src));
491                                 }
492                                 if let Level::Expect(expect_id) = level {
493                                     self.lint_expectations.push((
494                                         expect_id,
495                                         LintExpectation::new(reason, sp, false, tool_name),
496                                     ));
497                                 }
498                             }
499                             Err((None, _)) => {
500                                 // If Tool(Err(None, _)) is returned, then either the lint does not
501                                 // exist in the tool or the code was not compiled with the tool and
502                                 // therefore the lint was never added to the `LintStore`. To detect
503                                 // this is the responsibility of the lint tool.
504                             }
505                         }
506                     }
507
508                     &CheckLintNameResult::NoTool => {
509                         sess.emit_err(UnknownToolInScopedLint {
510                             span: tool_ident.map(|ident| ident.span),
511                             tool_name: tool_name.unwrap(),
512                             lint_name: pprust::path_to_string(&meta_item.path),
513                             is_nightly_build: sess.is_nightly_build().then_some(()),
514                         });
515                         continue;
516                     }
517
518                     _ if !self.warn_about_weird_lints => {}
519
520                     CheckLintNameResult::Warning(msg, renamed) => {
521                         let lint = builtin::RENAMED_AND_REMOVED_LINTS;
522                         let (renamed_lint_level, src) = self.sets.get_lint_level(
523                             lint,
524                             self.cur,
525                             Some(self.current_specs()),
526                             &sess,
527                         );
528                         struct_lint_level(
529                             self.sess,
530                             lint,
531                             renamed_lint_level,
532                             src,
533                             Some(sp.into()),
534                             |lint| {
535                                 let mut err = lint.build(msg);
536                                 if let Some(new_name) = &renamed {
537                                     err.span_suggestion(
538                                         sp,
539                                         "use the new name",
540                                         new_name,
541                                         Applicability::MachineApplicable,
542                                     );
543                                 }
544                                 err.emit();
545                             },
546                         );
547                     }
548                     CheckLintNameResult::NoLint(suggestion) => {
549                         let lint = builtin::UNKNOWN_LINTS;
550                         let (level, src) = self.sets.get_lint_level(
551                             lint,
552                             self.cur,
553                             Some(self.current_specs()),
554                             self.sess,
555                         );
556                         struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
557                             let name = if let Some(tool_ident) = tool_ident {
558                                 format!("{}::{}", tool_ident.name, name)
559                             } else {
560                                 name.to_string()
561                             };
562                             let mut db = lint.build(format!("unknown lint: `{}`", name));
563                             if let Some(suggestion) = suggestion {
564                                 db.span_suggestion(
565                                     sp,
566                                     "did you mean",
567                                     suggestion,
568                                     Applicability::MachineApplicable,
569                                 );
570                             }
571                             db.emit();
572                         });
573                     }
574                 }
575                 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
576                 // This happens outside of the match because the new lint should be applied even if
577                 // we don't warn about the name change.
578                 if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
579                     // Ignore any errors or warnings that happen because the new name is inaccurate
580                     // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
581                     if let CheckLintNameResult::Ok(ids) =
582                         self.store.check_lint_name(&new_name, None, self.registered_tools)
583                     {
584                         let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
585                         for &id in ids {
586                             if self.check_gated_lint(id, attr.span) {
587                                 self.insert_spec(id, (level, src));
588                             }
589                         }
590                         if let Level::Expect(expect_id) = level {
591                             self.lint_expectations.push((
592                                 expect_id,
593                                 LintExpectation::new(reason, sp, false, tool_name),
594                             ));
595                         }
596                     } else {
597                         panic!("renamed lint does not exist: {}", new_name);
598                     }
599                 }
600             }
601         }
602
603         if !is_crate_node {
604             for (id, &(level, ref src)) in self.current_specs().iter() {
605                 if !id.lint.crate_level_only {
606                     continue;
607                 }
608
609                 let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else {
610                     continue
611                 };
612
613                 let lint = builtin::UNUSED_ATTRIBUTES;
614                 let (lint_level, lint_src) =
615                     self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess);
616                 struct_lint_level(
617                     self.sess,
618                     lint,
619                     lint_level,
620                     lint_src,
621                     Some(lint_attr_span.into()),
622                     |lint| {
623                         let mut db = lint.build(&format!(
624                             "{}({}) is ignored unless specified at crate level",
625                             level.as_str(),
626                             lint_attr_name
627                         ));
628                         db.emit();
629                     },
630                 );
631                 // don't set a separate error for every lint in the group
632                 break;
633             }
634         }
635
636         if self.current_specs().is_empty() {
637             self.sets.list.pop();
638             self.cur = prev;
639         }
640
641         BuilderPush { prev, changed: prev != self.cur }
642     }
643
644     fn create_stable_id(
645         &mut self,
646         unstable_id: LintExpectationId,
647         hir_id: HirId,
648         attr_index: usize,
649     ) -> LintExpectationId {
650         let stable_id =
651             LintExpectationId::Stable { hir_id, attr_index: attr_index as u16, lint_index: None };
652
653         self.expectation_id_map.insert(unstable_id, stable_id);
654
655         stable_id
656     }
657
658     /// Checks if the lint is gated on a feature that is not enabled.
659     ///
660     /// Returns `true` if the lint's feature is enabled.
661     fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
662         if let Some(feature) = lint_id.lint.feature_gate {
663             if !self.sess.features_untracked().enabled(feature) {
664                 let lint = builtin::UNKNOWN_LINTS;
665                 let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
666                 struct_lint_level(self.sess, lint, level, src, Some(span.into()), |lint_db| {
667                     let mut db =
668                         lint_db.build(&format!("unknown lint: `{}`", lint_id.lint.name_lower()));
669                     db.note(&format!("the `{}` lint is unstable", lint_id.lint.name_lower(),));
670                     add_feature_diagnostics(&mut db, &self.sess.parse_sess, feature);
671                     db.emit();
672                 });
673                 return false;
674             }
675         }
676         true
677     }
678
679     /// Called after `push` when the scope of a set of attributes are exited.
680     pub fn pop(&mut self, push: BuilderPush) {
681         self.cur = push.prev;
682     }
683
684     /// Find the lint level for a lint.
685     pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) {
686         self.sets.get_lint_level(lint, self.cur, None, self.sess)
687     }
688
689     /// Used to emit a lint-related diagnostic based on the current state of
690     /// this lint context.
691     pub fn struct_lint(
692         &self,
693         lint: &'static Lint,
694         span: Option<MultiSpan>,
695         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
696     ) {
697         let (level, src) = self.lint_level(lint);
698         struct_lint_level(self.sess, lint, level, src, span, decorate)
699     }
700
701     /// Registers the ID provided with the current set of lints stored in
702     /// this context.
703     pub fn register_id(&mut self, id: HirId) {
704         self.id_to_set.insert(id, self.cur);
705     }
706
707     fn update_unstable_expectation_ids(&self) {
708         self.sess.diagnostic().update_unstable_expectation_id(&self.expectation_id_map);
709     }
710
711     pub fn build_map(self) -> LintLevelMap {
712         LintLevelMap {
713             sets: self.sets,
714             id_to_set: self.id_to_set,
715             lint_expectations: self.lint_expectations,
716         }
717     }
718 }
719
720 struct LintLevelMapBuilder<'tcx> {
721     levels: LintLevelsBuilder<'tcx>,
722     tcx: TyCtxt<'tcx>,
723 }
724
725 impl LintLevelMapBuilder<'_> {
726     fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F)
727     where
728         F: FnOnce(&mut Self),
729     {
730         let is_crate_hir = id == hir::CRATE_HIR_ID;
731         let attrs = self.tcx.hir().attrs(id);
732         let push = self.levels.push(attrs, is_crate_hir, Some(id));
733
734         if push.changed {
735             self.levels.register_id(id);
736         }
737         f(self);
738         self.levels.pop(push);
739     }
740 }
741
742 impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
743     type NestedFilter = nested_filter::All;
744
745     fn nested_visit_map(&mut self) -> Self::Map {
746         self.tcx.hir()
747     }
748
749     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
750         self.with_lint_attrs(param.hir_id, |builder| {
751             intravisit::walk_param(builder, param);
752         });
753     }
754
755     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
756         self.with_lint_attrs(it.hir_id(), |builder| {
757             intravisit::walk_item(builder, it);
758         });
759     }
760
761     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
762         self.with_lint_attrs(it.hir_id(), |builder| {
763             intravisit::walk_foreign_item(builder, it);
764         })
765     }
766
767     fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
768         // We will call `with_lint_attrs` when we walk
769         // the `StmtKind`. The outer statement itself doesn't
770         // define the lint levels.
771         intravisit::walk_stmt(self, e);
772     }
773
774     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
775         self.with_lint_attrs(e.hir_id, |builder| {
776             intravisit::walk_expr(builder, e);
777         })
778     }
779
780     fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
781         self.with_lint_attrs(field.hir_id, |builder| {
782             intravisit::walk_expr_field(builder, field);
783         })
784     }
785
786     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
787         self.with_lint_attrs(s.hir_id, |builder| {
788             intravisit::walk_field_def(builder, s);
789         })
790     }
791
792     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
793         self.with_lint_attrs(v.id, |builder| {
794             intravisit::walk_variant(builder, v);
795         })
796     }
797
798     fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
799         self.with_lint_attrs(l.hir_id, |builder| {
800             intravisit::walk_local(builder, l);
801         })
802     }
803
804     fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
805         self.with_lint_attrs(a.hir_id, |builder| {
806             intravisit::walk_arm(builder, a);
807         })
808     }
809
810     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
811         self.with_lint_attrs(trait_item.hir_id(), |builder| {
812             intravisit::walk_trait_item(builder, trait_item);
813         });
814     }
815
816     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
817         self.with_lint_attrs(impl_item.hir_id(), |builder| {
818             intravisit::walk_impl_item(builder, impl_item);
819         });
820     }
821
822     fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
823         self.with_lint_attrs(field.hir_id, |builder| {
824             intravisit::walk_pat_field(builder, field);
825         })
826     }
827
828     fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
829         self.with_lint_attrs(p.hir_id, |builder| {
830             intravisit::walk_generic_param(builder, p);
831         });
832     }
833 }
834
835 pub fn provide(providers: &mut Providers) {
836     providers.lint_levels = lint_levels;
837 }