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