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