1use rustc_ast::attr::AttributeExt;
2use rustc_ast_pretty::pprust;
3use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4use rustc_data_structures::unord::UnordSet;
5use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
6use rustc_feature::{Features, GateIssue};
7use rustc_hir::HirId;
8use rustc_hir::intravisit::{self, Visitor};
9use rustc_index::IndexVec;
10use rustc_middle::bug;
11use rustc_middle::hir::nested_filter;
12use rustc_middle::lint::{
13 LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, lint_level,
14 reveal_actual_level,
15};
16use rustc_middle::query::Providers;
17use rustc_middle::ty::{RegisteredTools, TyCtxt};
18use rustc_session::Session;
19use rustc_session::lint::builtin::{
20 self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
21 UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
22};
23use rustc_session::lint::{Level, Lint, LintExpectationId, LintId};
24use rustc_span::{DUMMY_SP, Span, Symbol, sym};
25use tracing::{debug, instrument};
26use {rustc_ast as ast, rustc_hir as hir};
27
28use crate::builtin::MISSING_DOCS;
29use crate::context::{CheckLintNameResult, LintStore};
30use crate::errors::{
31 CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
32 OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
33};
34use crate::fluent_generated as fluent;
35use crate::late::unerased_lint_store;
36use crate::lints::{
37 DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
38 OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
39 RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLint, UnknownLintFromCommandLine,
40 UnknownLintSuggestion,
41};
42
43#[derive(Debug)]
47struct LintLevelSets {
48 list: IndexVec<LintStackIndex, LintSet>,
50}
51
52rustc_index::newtype_index! {
53 struct LintStackIndex {
54 const COMMAND_LINE = 0;
55 }
56}
57
58#[derive(Debug)]
67struct LintSet {
68 specs: FxIndexMap<LintId, LevelAndSource>,
71 parent: LintStackIndex,
72}
73
74impl LintLevelSets {
75 fn new() -> Self {
76 LintLevelSets { list: IndexVec::new() }
77 }
78
79 fn get_lint_level(
80 &self,
81 lint: &'static Lint,
82 idx: LintStackIndex,
83 aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
84 sess: &Session,
85 ) -> LevelAndSource {
86 let lint = LintId::of(lint);
87 let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
88 let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
89 self.raw_lint_id_level(id, idx, aux)
90 });
91 LevelAndSource { level, lint_id, src }
92 }
93
94 fn raw_lint_id_level(
95 &self,
96 id: LintId,
97 mut idx: LintStackIndex,
98 aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
99 ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
100 if let Some(specs) = aux
101 && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
102 {
103 return (Some((level, lint_id)), src);
104 }
105
106 loop {
107 let LintSet { ref specs, parent } = self.list[idx];
108 if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
109 return (Some((level, lint_id)), src);
110 }
111 if idx == COMMAND_LINE {
112 return (None, LintLevelSource::Default);
113 }
114 idx = parent;
115 }
116 }
117}
118
119fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
120 let store = unerased_lint_store(&tcx.sess);
121 let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
122
123 let mut dont_need_to_run: FxHashSet<LintId> = store
124 .get_lints()
125 .into_iter()
126 .filter(|lint| {
127 let has_future_breakage =
129 lint.future_incompatible.is_some_and(|fut| fut.report_in_deps);
130 !has_future_breakage && !lint.eval_always
131 })
132 .filter(|lint| {
133 let lint_level =
134 root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
135 matches!(lint_level.level, Level::Allow)
137 || (matches!(lint_level.src, LintLevelSource::Default)
138 && lint.default_level(tcx.sess.edition()) == Level::Allow)
139 })
140 .map(|lint| LintId::of(*lint))
141 .collect();
142
143 for owner in tcx.hir_crate_items(()).owners() {
144 let map = tcx.shallow_lint_levels_on(owner);
145
146 for (_, specs) in map.specs.iter() {
148 for (lint, level_and_source) in specs.iter() {
149 if !matches!(level_and_source.level, Level::Allow) {
150 dont_need_to_run.remove(lint);
151 }
152 }
153 }
154 }
155
156 dont_need_to_run.into()
157}
158
159#[instrument(level = "trace", skip(tcx), ret)]
160fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
161 let store = unerased_lint_store(tcx.sess);
162 let attrs = tcx.hir_attr_map(owner);
163
164 let mut levels = LintLevelsBuilder {
165 sess: tcx.sess,
166 features: tcx.features(),
167 provider: LintLevelQueryMap {
168 tcx,
169 cur: owner.into(),
170 specs: ShallowLintLevelMap::default(),
171 empty: FxIndexMap::default(),
172 attrs,
173 },
174 lint_added_lints: false,
175 store,
176 registered_tools: tcx.registered_tools(()),
177 };
178
179 if owner == hir::CRATE_OWNER_ID {
180 levels.add_command_line();
181 }
182
183 match attrs.map.range(..) {
184 [] => {}
186 &[(local_id, _)] => levels.add_id(HirId { owner, local_id }),
188 _ => match tcx.hir_owner_node(owner) {
192 hir::OwnerNode::Item(item) => levels.visit_item(item),
193 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
194 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
195 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
196 hir::OwnerNode::Crate(mod_) => {
197 levels.add_id(hir::CRATE_HIR_ID);
198 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
199 }
200 hir::OwnerNode::Synthetic => unreachable!(),
201 },
202 }
203
204 let specs = levels.provider.specs;
205
206 #[cfg(debug_assertions)]
207 for (_, v) in specs.specs.iter() {
208 debug_assert!(!v.is_empty());
209 }
210
211 specs
212}
213
214pub struct TopDown {
215 sets: LintLevelSets,
216 cur: LintStackIndex,
217}
218
219pub trait LintLevelsProvider {
220 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource>;
221 fn insert(&mut self, id: LintId, lvl: LevelAndSource);
222 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
223 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation);
224}
225
226impl LintLevelsProvider for TopDown {
227 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
228 &self.sets.list[self.cur].specs
229 }
230
231 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
232 self.sets.list[self.cur].specs.insert(id, lvl);
233 }
234
235 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
236 self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
237 }
238
239 fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {}
240}
241
242struct LintLevelQueryMap<'tcx> {
243 tcx: TyCtxt<'tcx>,
244 cur: HirId,
245 specs: ShallowLintLevelMap,
246 empty: FxIndexMap<LintId, LevelAndSource>,
248 attrs: &'tcx hir::AttributeMap<'tcx>,
249}
250
251impl LintLevelsProvider for LintLevelQueryMap<'_> {
252 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
253 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
254 }
255 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
256 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
257 }
258 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
259 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
260 }
261 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
262 self.specs.expectations.push((id, expectation))
263 }
264}
265
266impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
267 fn add_id(&mut self, hir_id: HirId) {
268 self.provider.cur = hir_id;
269 self.add(
270 self.provider.attrs.get(hir_id.local_id),
271 hir_id == hir::CRATE_HIR_ID,
272 Some(hir_id),
273 );
274 }
275}
276
277impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
278 type NestedFilter = nested_filter::OnlyBodies;
279
280 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
281 self.provider.tcx
282 }
283
284 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
285 self.add_id(param.hir_id);
286 intravisit::walk_param(self, param);
287 }
288
289 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
290 self.add_id(it.hir_id());
291 intravisit::walk_item(self, it);
292 }
293
294 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
295 self.add_id(it.hir_id());
296 intravisit::walk_foreign_item(self, it);
297 }
298
299 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
300 self.add_id(s.hir_id);
301 intravisit::walk_stmt(self, s);
302 }
303
304 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
305 self.add_id(e.hir_id);
306 intravisit::walk_expr(self, e);
307 }
308
309 fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result {
310 self.add_id(f.hir_id);
311 intravisit::walk_pat_field(self, f);
312 }
313
314 fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
315 self.add_id(f.hir_id);
316 intravisit::walk_expr_field(self, f);
317 }
318
319 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
320 self.add_id(s.hir_id);
321 intravisit::walk_field_def(self, s);
322 }
323
324 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
325 self.add_id(v.hir_id);
326 intravisit::walk_variant(self, v);
327 }
328
329 fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
330 self.add_id(l.hir_id);
331 intravisit::walk_local(self, l);
332 }
333
334 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
335 self.add_id(a.hir_id);
336 intravisit::walk_arm(self, a);
337 }
338
339 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
340 self.add_id(trait_item.hir_id());
341 intravisit::walk_trait_item(self, trait_item);
342 }
343
344 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
345 self.add_id(impl_item.hir_id());
346 intravisit::walk_impl_item(self, impl_item);
347 }
348}
349
350pub struct LintLevelsBuilder<'s, P> {
351 sess: &'s Session,
352 features: &'s Features,
353 provider: P,
354 lint_added_lints: bool,
355 store: &'s LintStore,
356 registered_tools: &'s RegisteredTools,
357}
358
359pub(crate) struct BuilderPush {
360 prev: LintStackIndex,
361}
362
363impl<'s> LintLevelsBuilder<'s, TopDown> {
364 pub(crate) fn new(
365 sess: &'s Session,
366 features: &'s Features,
367 lint_added_lints: bool,
368 store: &'s LintStore,
369 registered_tools: &'s RegisteredTools,
370 ) -> Self {
371 let mut builder = LintLevelsBuilder {
372 sess,
373 features,
374 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
375 lint_added_lints,
376 store,
377 registered_tools,
378 };
379 builder.process_command_line();
380 assert_eq!(builder.provider.sets.list.len(), 1);
381 builder
382 }
383
384 pub fn crate_root(
385 sess: &'s Session,
386 features: &'s Features,
387 lint_added_lints: bool,
388 store: &'s LintStore,
389 registered_tools: &'s RegisteredTools,
390 crate_attrs: &[ast::Attribute],
391 ) -> Self {
392 let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
393 builder.add(crate_attrs, true, None);
394 builder
395 }
396
397 fn process_command_line(&mut self) {
398 self.provider.cur = self
399 .provider
400 .sets
401 .list
402 .push(LintSet { specs: FxIndexMap::default(), parent: COMMAND_LINE });
403 self.add_command_line();
404 }
405
406 pub(crate) fn push(
421 &mut self,
422 attrs: &[ast::Attribute],
423 is_crate_node: bool,
424 source_hir_id: Option<HirId>,
425 ) -> BuilderPush {
426 let prev = self.provider.cur;
427 self.provider.cur =
428 self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
429
430 self.add(attrs, is_crate_node, source_hir_id);
431
432 if self.provider.current_specs().is_empty() {
433 self.provider.sets.list.pop();
434 self.provider.cur = prev;
435 }
436
437 BuilderPush { prev }
438 }
439
440 pub(crate) fn pop(&mut self, push: BuilderPush) {
442 self.provider.cur = push.prev;
443 std::mem::forget(push);
444 }
445}
446
447#[cfg(debug_assertions)]
448impl Drop for BuilderPush {
449 fn drop(&mut self) {
450 panic!("Found a `push` without a `pop`.");
451 }
452}
453
454impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
455 pub(crate) fn sess(&self) -> &Session {
456 self.sess
457 }
458
459 pub(crate) fn features(&self) -> &Features {
460 self.features
461 }
462
463 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
464 self.provider.current_specs()
465 }
466
467 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
468 self.provider.insert(id, lvl)
469 }
470
471 fn add_command_line(&mut self) {
472 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
473 let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
475 if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) {
476 self.sess
477 .dcx()
478 .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
479 }
480 match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
481 CheckLintNameResult::Renamed(ref replace) => {
482 let name = lint_name.as_str();
483 let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
484 let requested_level = RequestedLevel { level, lint_name };
485 let lint =
486 RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
487 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
488 }
489 CheckLintNameResult::Removed(ref reason) => {
490 let name = lint_name.as_str();
491 let requested_level = RequestedLevel { level, lint_name };
492 let lint = RemovedLintFromCommandLine { name, reason, requested_level };
493 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
494 }
495 CheckLintNameResult::NoLint(suggestion) => {
496 let name = lint_name.clone();
497 let suggestion = suggestion.map(|(replace, from_rustc)| {
498 UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
499 });
500 let requested_level = RequestedLevel { level, lint_name };
501 let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
502 self.emit_lint(UNKNOWN_LINTS, lint);
503 }
504 CheckLintNameResult::Tool(_, Some(ref replace)) => {
505 let name = lint_name.clone();
506 let requested_level = RequestedLevel { level, lint_name };
507 let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
508 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
509 }
510 CheckLintNameResult::NoTool => {
511 self.sess.dcx().emit_err(CheckNameUnknownTool {
512 tool_name: tool_name.unwrap(),
513 sub: RequestedLevel { level, lint_name },
514 });
515 }
516 _ => {}
517 };
518
519 let lint_flag_val = Symbol::intern(lint_name);
520
521 let Some(ids) = self.store.find_lints(lint_name) else {
522 continue;
524 };
525 for &id in ids {
526 if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
528 self.current_specs().get(&id)
529 {
530 continue;
531 }
532
533 if self.check_gated_lint(id, DUMMY_SP, true) {
534 let src = LintLevelSource::CommandLine(lint_flag_val, level);
535 self.insert(id, LevelAndSource { level, lint_id: None, src });
536 }
537 }
538 }
539 }
540
541 fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
545 let LevelAndSource { level: old_level, src: old_src, .. } =
546 self.provider.get_lint_level(id.lint, self.sess);
547
548 if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
555 return;
557 } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
558 let id_name = id.lint.name_lower();
565 let fcw_warning = match old_src {
566 LintLevelSource::Default => false,
567 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
568 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
569 };
570 debug!(
571 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
572 fcw_warning,
573 self.current_specs(),
574 old_src,
575 id_name
576 );
577 let sub = match old_src {
578 LintLevelSource::Default => {
579 OverruledAttributeSub::DefaultSource { id: id.to_string() }
580 }
581 LintLevelSource::Node { span, reason, .. } => {
582 OverruledAttributeSub::NodeSource { span, reason }
583 }
584 LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
585 };
586 if !fcw_warning {
587 self.sess.dcx().emit_err(OverruledAttribute {
588 span: src.span(),
589 overruled: src.span(),
590 lint_level: level.as_str(),
591 lint_source: src.name(),
592 sub,
593 });
594 } else {
595 self.emit_span_lint(
596 FORBIDDEN_LINT_GROUPS,
597 src.span().into(),
598 OverruledAttributeLint {
599 overruled: src.span(),
600 lint_level: level.as_str(),
601 lint_source: src.name(),
602 sub,
603 },
604 );
605 }
606
607 if !fcw_warning {
611 return;
612 }
613 }
614
615 if let Level::Expect = level
619 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
620 {
621 return;
622 }
623
624 match (old_level, level) {
625 (Level::ForceWarn, Level::Expect) => {
627 self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
628 }
629 (Level::ForceWarn, _) => self.insert(
631 id,
632 LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
633 ),
634 _ => self.insert(id, LevelAndSource { level, lint_id, src }),
636 };
637 }
638
639 fn add(
640 &mut self,
641 attrs: &[impl AttributeExt],
642 is_crate_node: bool,
643 source_hir_id: Option<HirId>,
644 ) {
645 let sess = self.sess;
646 for (attr_index, attr) in attrs.iter().enumerate() {
647 if attr.has_name(sym::automatically_derived) {
648 self.insert(
649 LintId::of(SINGLE_USE_LIFETIMES),
650 LevelAndSource {
651 level: Level::Allow,
652 lint_id: None,
653 src: LintLevelSource::Default,
654 },
655 );
656 continue;
657 }
658
659 if attr.has_name(sym::doc)
661 && attr
662 .meta_item_list()
663 .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
664 {
665 self.insert(
666 LintId::of(MISSING_DOCS),
667 LevelAndSource {
668 level: Level::Allow,
669 lint_id: None,
670 src: LintLevelSource::Default,
671 },
672 );
673 continue;
674 }
675
676 let (level, lint_id) = match Level::from_attr(attr) {
677 None => continue,
678 Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
681 let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
682 else {
683 bug!("stable id Level::from_attr")
684 };
685
686 let stable_id = LintExpectationId::Stable {
687 hir_id,
688 attr_index: attr_index.try_into().unwrap(),
689 lint_index: None,
690 };
691
692 (Level::Expect, Some(stable_id))
693 }
694 Some((lvl, id)) => (lvl, id),
695 };
696
697 let Some(mut metas) = attr.meta_item_list() else { continue };
698
699 let Some(tail_li) = metas.last() else {
701 continue;
703 };
704
705 let mut reason = None;
708 if let Some(item) = tail_li.meta_item() {
709 match item.kind {
710 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
712 if item.path == sym::reason {
713 if let ast::LitKind::Str(rationale, _) = name_value.kind {
714 reason = Some(rationale);
715 } else {
716 sess.dcx().emit_err(MalformedAttribute {
717 span: name_value.span,
718 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
719 name_value.span,
720 ),
721 });
722 }
723 metas.pop().unwrap();
725 } else {
726 sess.dcx().emit_err(MalformedAttribute {
727 span: item.span,
728 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
729 });
730 }
731 }
732 ast::MetaItemKind::List(_) => {
733 sess.dcx().emit_err(MalformedAttribute {
734 span: item.span,
735 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
736 });
737 }
738 }
739 }
740
741 for (lint_index, li) in metas.iter_mut().enumerate() {
742 let mut lint_id = lint_id;
743 if let Some(id) = &mut lint_id {
744 id.set_lint_index(Some(lint_index as u16));
745 }
746
747 let sp = li.span();
748 let meta_item = match li {
749 ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
750 _ => {
751 let sub = if let Some(item) = li.meta_item()
752 && let ast::MetaItemKind::NameValue(_) = item.kind
753 && item.path == sym::reason
754 {
755 MalformedAttributeSub::ReasonMustComeLast(sp)
756 } else {
757 MalformedAttributeSub::BadAttributeArgument(sp)
758 };
759
760 sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
761 continue;
762 }
763 };
764 let tool_ident = if meta_item.path.segments.len() > 1 {
765 Some(meta_item.path.segments.remove(0).ident)
766 } else {
767 None
768 };
769 let tool_name = tool_ident.map(|ident| ident.name);
770 let name = pprust::path_to_string(&meta_item.path);
771 let lint_result =
772 self.store.check_lint_name(&name, tool_name, self.registered_tools);
773
774 let (ids, name) = match lint_result {
775 CheckLintNameResult::Ok(ids) => {
776 let name =
777 meta_item.path.segments.last().expect("empty lint name").ident.name;
778 (ids, name)
779 }
780
781 CheckLintNameResult::Tool(ids, new_lint_name) => {
782 let name = match new_lint_name {
783 None => {
784 let complete_name =
785 &format!("{}::{}", tool_ident.unwrap().name, name);
786 Symbol::intern(complete_name)
787 }
788 Some(new_lint_name) => {
789 self.emit_span_lint(
790 builtin::RENAMED_AND_REMOVED_LINTS,
791 sp.into(),
792 DeprecatedLintName {
793 name,
794 suggestion: sp,
795 replace: &new_lint_name,
796 },
797 );
798 Symbol::intern(&new_lint_name)
799 }
800 };
801 (ids, name)
802 }
803
804 CheckLintNameResult::MissingTool => {
805 continue;
810 }
811
812 CheckLintNameResult::NoTool => {
813 sess.dcx().emit_err(UnknownToolInScopedLint {
814 span: tool_ident.map(|ident| ident.span),
815 tool_name: tool_name.unwrap(),
816 lint_name: pprust::path_to_string(&meta_item.path),
817 is_nightly_build: sess.is_nightly_build(),
818 });
819 continue;
820 }
821
822 CheckLintNameResult::Renamed(ref replace) => {
823 if self.lint_added_lints {
824 let suggestion =
825 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
826 let name =
827 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
828 let lint = RenamedLint { name: name.as_str(), replace, suggestion };
829 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
830 }
831
832 let CheckLintNameResult::Ok(ids) =
838 self.store.check_lint_name(replace, None, self.registered_tools)
839 else {
840 panic!("renamed lint does not exist: {replace}");
841 };
842
843 (ids, Symbol::intern(&replace))
844 }
845
846 CheckLintNameResult::Removed(ref reason) => {
847 if self.lint_added_lints {
848 let name =
849 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
850 let lint = RemovedLint { name: name.as_str(), reason };
851 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
852 }
853 continue;
854 }
855
856 CheckLintNameResult::NoLint(suggestion) => {
857 if self.lint_added_lints {
858 let name =
859 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
860 let suggestion = suggestion.map(|(replace, from_rustc)| {
861 UnknownLintSuggestion::WithSpan {
862 suggestion: sp,
863 replace,
864 from_rustc,
865 }
866 });
867 let lint = UnknownLint { name, suggestion };
868 self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint);
869 }
870 continue;
871 }
872 };
873
874 let src = LintLevelSource::Node { name, span: sp, reason };
875 for &id in ids {
876 if self.check_gated_lint(id, sp, false) {
877 self.insert_spec(id, LevelAndSource { level, lint_id, src });
878 }
879 }
880
881 if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
887 let is_unfulfilled_lint_expectations = match ids {
891 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
892 _ => false,
893 };
894 self.provider.push_expectation(
895 expect_id,
896 LintExpectation::new(
897 reason,
898 sp,
899 is_unfulfilled_lint_expectations,
900 tool_name,
901 ),
902 );
903 }
904 }
905 }
906
907 if self.lint_added_lints && !is_crate_node {
908 for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
909 if !id.lint.crate_level_only {
910 continue;
911 }
912
913 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
914 else {
915 continue;
916 };
917
918 self.emit_span_lint(
919 UNUSED_ATTRIBUTES,
920 lint_attr_span.into(),
921 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
922 );
923 break;
925 }
926 }
927 }
928
929 #[track_caller]
933 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
934 let feature = if let Some(feature) = lint_id.lint.feature_gate
935 && !self.features.enabled(feature)
936 {
937 feature
939 } else {
940 return true;
942 };
943
944 if self.lint_added_lints {
945 let lint = builtin::UNKNOWN_LINTS;
946 let level = self.lint_level(builtin::UNKNOWN_LINTS);
947 #[allow(rustc::diagnostic_outside_of_impl)]
949 lint_level(self.sess, lint, level, Some(span.into()), |lint| {
950 lint.primary_message(fluent::lint_unknown_gated_lint);
951 lint.arg("name", lint_id.lint.name_lower());
952 lint.note(fluent::lint_note);
953 rustc_session::parse::add_feature_diagnostics_for_issue(
954 lint,
955 &self.sess,
956 feature,
957 GateIssue::Language,
958 lint_from_cli,
959 None,
960 );
961 });
962 }
963
964 false
965 }
966
967 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
969 self.provider.get_lint_level(lint, self.sess)
970 }
971
972 #[rustc_lint_diagnostics]
977 #[track_caller]
978 pub(crate) fn opt_span_lint(
979 &self,
980 lint: &'static Lint,
981 span: Option<MultiSpan>,
982 decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
983 ) {
984 let level = self.lint_level(lint);
985 lint_level(self.sess, lint, level, span, decorate)
986 }
987
988 #[track_caller]
989 pub fn emit_span_lint(
990 &self,
991 lint: &'static Lint,
992 span: MultiSpan,
993 decorate: impl for<'a> LintDiagnostic<'a, ()>,
994 ) {
995 let level = self.lint_level(lint);
996 lint_level(self.sess, lint, level, Some(span), |lint| {
997 decorate.decorate_lint(lint);
998 });
999 }
1000
1001 #[track_caller]
1002 pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
1003 let level = self.lint_level(lint);
1004 lint_level(self.sess, lint, level, None, |lint| {
1005 decorate.decorate_lint(lint);
1006 });
1007 }
1008}
1009
1010pub(crate) fn provide(providers: &mut Providers) {
1011 *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
1012}
1013
1014pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1015 match lint_name.split_once("::") {
1016 Some((tool_name, lint_name)) => {
1017 let tool_name = Symbol::intern(tool_name);
1018
1019 (Some(tool_name), lint_name)
1020 }
1021 None => (None, lint_name),
1022 }
1023}