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.reason.has_future_breakage());
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 = RenamedLintFromCommandLine { name, suggestion, requested_level };
486 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
487 }
488 CheckLintNameResult::Removed(ref reason) => {
489 let name = lint_name.as_str();
490 let requested_level = RequestedLevel { level, lint_name };
491 let lint = RemovedLintFromCommandLine { name, reason, requested_level };
492 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
493 }
494 CheckLintNameResult::NoLint(suggestion) => {
495 let name = lint_name.clone();
496 let suggestion = suggestion.map(|(replace, from_rustc)| {
497 UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
498 });
499 let requested_level = RequestedLevel { level, lint_name };
500 let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
501 self.emit_lint(UNKNOWN_LINTS, lint);
502 }
503 CheckLintNameResult::Tool(_, Some(ref replace)) => {
504 let name = lint_name.clone();
505 let requested_level = RequestedLevel { level, lint_name };
506 let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
507 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
508 }
509 CheckLintNameResult::NoTool => {
510 self.sess.dcx().emit_err(CheckNameUnknownTool {
511 tool_name: tool_name.unwrap(),
512 sub: RequestedLevel { level, lint_name },
513 });
514 }
515 _ => {}
516 };
517
518 let lint_flag_val = Symbol::intern(lint_name);
519
520 let Some(ids) = self.store.find_lints(lint_name) else {
521 continue;
523 };
524 for &id in ids {
525 if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
527 self.current_specs().get(&id)
528 {
529 continue;
530 }
531
532 if self.check_gated_lint(id, DUMMY_SP, true) {
533 let src = LintLevelSource::CommandLine(lint_flag_val, level);
534 self.insert(id, LevelAndSource { level, lint_id: None, src });
535 }
536 }
537 }
538 }
539
540 fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
544 let LevelAndSource { level: old_level, src: old_src, .. } =
545 self.provider.get_lint_level(id.lint, self.sess);
546
547 if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
554 return;
556 } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
557 let id_name = id.lint.name_lower();
564 let fcw_warning = match old_src {
565 LintLevelSource::Default => false,
566 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
567 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
568 };
569 debug!(
570 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
571 fcw_warning,
572 self.current_specs(),
573 old_src,
574 id_name
575 );
576 let sub = match old_src {
577 LintLevelSource::Default => {
578 OverruledAttributeSub::DefaultSource { id: id.to_string() }
579 }
580 LintLevelSource::Node { span, reason, .. } => {
581 OverruledAttributeSub::NodeSource { span, reason }
582 }
583 LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
584 };
585 if !fcw_warning {
586 self.sess.dcx().emit_err(OverruledAttribute {
587 span: src.span(),
588 overruled: src.span(),
589 lint_level: level.as_str(),
590 lint_source: src.name(),
591 sub,
592 });
593 } else {
594 self.emit_span_lint(
595 FORBIDDEN_LINT_GROUPS,
596 src.span().into(),
597 OverruledAttributeLint {
598 overruled: src.span(),
599 lint_level: level.as_str(),
600 lint_source: src.name(),
601 sub,
602 },
603 );
604 }
605
606 if !fcw_warning {
610 return;
611 }
612 }
613
614 if let Level::Expect = level
618 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
619 {
620 return;
621 }
622
623 match (old_level, level) {
624 (Level::ForceWarn, Level::Expect) => {
626 self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
627 }
628 (Level::ForceWarn, _) => self.insert(
630 id,
631 LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
632 ),
633 _ => self.insert(id, LevelAndSource { level, lint_id, src }),
635 };
636 }
637
638 fn add(
639 &mut self,
640 attrs: &[impl AttributeExt],
641 is_crate_node: bool,
642 source_hir_id: Option<HirId>,
643 ) {
644 let sess = self.sess;
645 for (attr_index, attr) in attrs.iter().enumerate() {
646 if attr.has_name(sym::automatically_derived) {
647 self.insert(
648 LintId::of(SINGLE_USE_LIFETIMES),
649 LevelAndSource {
650 level: Level::Allow,
651 lint_id: None,
652 src: LintLevelSource::Default,
653 },
654 );
655 continue;
656 }
657
658 if attr.has_name(sym::doc)
660 && attr
661 .meta_item_list()
662 .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
663 {
664 self.insert(
665 LintId::of(MISSING_DOCS),
666 LevelAndSource {
667 level: Level::Allow,
668 lint_id: None,
669 src: LintLevelSource::Default,
670 },
671 );
672 continue;
673 }
674
675 let (level, lint_id) = match Level::from_attr(attr) {
676 None => continue,
677 Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
680 let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
681 else {
682 bug!("stable id Level::from_attr")
683 };
684
685 let stable_id = LintExpectationId::Stable {
686 hir_id,
687 attr_index: attr_index.try_into().unwrap(),
688 lint_index: None,
689 };
690
691 (Level::Expect, Some(stable_id))
692 }
693 Some((lvl, id)) => (lvl, id),
694 };
695
696 let Some(mut metas) = attr.meta_item_list() else { continue };
697
698 let Some(tail_li) = metas.last() else {
700 continue;
702 };
703
704 let mut reason = None;
707 if let Some(item) = tail_li.meta_item() {
708 match item.kind {
709 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
711 if item.path == sym::reason {
712 if let ast::LitKind::Str(rationale, _) = name_value.kind {
713 reason = Some(rationale);
714 } else {
715 sess.dcx().emit_err(MalformedAttribute {
716 span: name_value.span,
717 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
718 name_value.span,
719 ),
720 });
721 }
722 metas.pop().unwrap();
724 } else {
725 sess.dcx().emit_err(MalformedAttribute {
726 span: item.span,
727 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
728 });
729 }
730 }
731 ast::MetaItemKind::List(_) => {
732 sess.dcx().emit_err(MalformedAttribute {
733 span: item.span,
734 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
735 });
736 }
737 }
738 }
739
740 for (lint_index, li) in metas.iter_mut().enumerate() {
741 let mut lint_id = lint_id;
742 if let Some(id) = &mut lint_id {
743 id.set_lint_index(Some(lint_index as u16));
744 }
745
746 let sp = li.span();
747 let meta_item = match li {
748 ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
749 _ => {
750 let sub = if let Some(item) = li.meta_item()
751 && let ast::MetaItemKind::NameValue(_) = item.kind
752 && item.path == sym::reason
753 {
754 MalformedAttributeSub::ReasonMustComeLast(sp)
755 } else {
756 MalformedAttributeSub::BadAttributeArgument(sp)
757 };
758
759 sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
760 continue;
761 }
762 };
763 let tool_ident = if meta_item.path.segments.len() > 1 {
764 Some(meta_item.path.segments.remove(0).ident)
765 } else {
766 None
767 };
768 let tool_name = tool_ident.map(|ident| ident.name);
769 let name = pprust::path_to_string(&meta_item.path);
770 let lint_result =
771 self.store.check_lint_name(&name, tool_name, self.registered_tools);
772
773 let (ids, name) = match lint_result {
774 CheckLintNameResult::Ok(ids) => {
775 let name =
776 meta_item.path.segments.last().expect("empty lint name").ident.name;
777 (ids, name)
778 }
779
780 CheckLintNameResult::Tool(ids, new_lint_name) => {
781 let name = match new_lint_name {
782 None => {
783 let complete_name =
784 &format!("{}::{}", tool_ident.unwrap().name, name);
785 Symbol::intern(complete_name)
786 }
787 Some(new_lint_name) => {
788 self.emit_span_lint(
789 builtin::RENAMED_AND_REMOVED_LINTS,
790 sp.into(),
791 DeprecatedLintName {
792 name,
793 suggestion: sp,
794 replace: &new_lint_name,
795 },
796 );
797 Symbol::intern(&new_lint_name)
798 }
799 };
800 (ids, name)
801 }
802
803 CheckLintNameResult::MissingTool => {
804 continue;
809 }
810
811 CheckLintNameResult::NoTool => {
812 sess.dcx().emit_err(UnknownToolInScopedLint {
813 span: tool_ident.map(|ident| ident.span),
814 tool_name: tool_name.unwrap(),
815 lint_name: pprust::path_to_string(&meta_item.path),
816 is_nightly_build: sess.is_nightly_build(),
817 });
818 continue;
819 }
820
821 CheckLintNameResult::Renamed(ref replace) => {
822 if self.lint_added_lints {
823 let suggestion =
824 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
825 let name =
826 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
827 let lint = RenamedLint { name: name.as_str(), suggestion };
828 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
829 }
830
831 let CheckLintNameResult::Ok(ids) =
837 self.store.check_lint_name(replace, None, self.registered_tools)
838 else {
839 panic!("renamed lint does not exist: {replace}");
840 };
841
842 (ids, Symbol::intern(&replace))
843 }
844
845 CheckLintNameResult::Removed(ref reason) => {
846 if self.lint_added_lints {
847 let name =
848 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
849 let lint = RemovedLint { name: name.as_str(), reason };
850 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
851 }
852 continue;
853 }
854
855 CheckLintNameResult::NoLint(suggestion) => {
856 if self.lint_added_lints {
857 let name =
858 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
859 let suggestion = suggestion.map(|(replace, from_rustc)| {
860 UnknownLintSuggestion::WithSpan {
861 suggestion: sp,
862 replace,
863 from_rustc,
864 }
865 });
866 let lint = UnknownLint { name, suggestion };
867 self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint);
868 }
869 continue;
870 }
871 };
872
873 let src = LintLevelSource::Node { name, span: sp, reason };
874 for &id in ids {
875 if self.check_gated_lint(id, sp, false) {
876 self.insert_spec(id, LevelAndSource { level, lint_id, src });
877 }
878 }
879
880 if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
886 let is_unfulfilled_lint_expectations = match ids {
890 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
891 _ => false,
892 };
893 self.provider.push_expectation(
894 expect_id,
895 LintExpectation::new(
896 reason,
897 sp,
898 is_unfulfilled_lint_expectations,
899 tool_name,
900 ),
901 );
902 }
903 }
904 }
905
906 if self.lint_added_lints && !is_crate_node {
907 for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
908 if !id.lint.crate_level_only {
909 continue;
910 }
911
912 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
913 else {
914 continue;
915 };
916
917 self.emit_span_lint(
918 UNUSED_ATTRIBUTES,
919 lint_attr_span.into(),
920 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
921 );
922 break;
924 }
925 }
926 }
927
928 #[track_caller]
932 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
933 let feature = if let Some(feature) = lint_id.lint.feature_gate
934 && !self.features.enabled(feature)
935 {
936 feature
938 } else {
939 return true;
941 };
942
943 if self.lint_added_lints {
944 let lint = builtin::UNKNOWN_LINTS;
945 let level = self.lint_level(builtin::UNKNOWN_LINTS);
946 #[allow(rustc::diagnostic_outside_of_impl)]
948 lint_level(self.sess, lint, level, Some(span.into()), |lint| {
949 lint.primary_message(fluent::lint_unknown_gated_lint);
950 lint.arg("name", lint_id.lint.name_lower());
951 lint.note(fluent::lint_note);
952 rustc_session::parse::add_feature_diagnostics_for_issue(
953 lint,
954 &self.sess,
955 feature,
956 GateIssue::Language,
957 lint_from_cli,
958 None,
959 );
960 });
961 }
962
963 false
964 }
965
966 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
968 self.provider.get_lint_level(lint, self.sess)
969 }
970
971 #[rustc_lint_diagnostics]
976 #[track_caller]
977 pub(crate) fn opt_span_lint(
978 &self,
979 lint: &'static Lint,
980 span: Option<MultiSpan>,
981 decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
982 ) {
983 let level = self.lint_level(lint);
984 lint_level(self.sess, lint, level, span, decorate)
985 }
986
987 #[track_caller]
988 pub fn emit_span_lint(
989 &self,
990 lint: &'static Lint,
991 span: MultiSpan,
992 decorate: impl for<'a> LintDiagnostic<'a, ()>,
993 ) {
994 let level = self.lint_level(lint);
995 lint_level(self.sess, lint, level, Some(span), |lint| {
996 decorate.decorate_lint(lint);
997 });
998 }
999
1000 #[track_caller]
1001 pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
1002 let level = self.lint_level(lint);
1003 lint_level(self.sess, lint, level, None, |lint| {
1004 decorate.decorate_lint(lint);
1005 });
1006 }
1007}
1008
1009pub(crate) fn provide(providers: &mut Providers) {
1010 *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
1011}
1012
1013pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1014 match lint_name.split_once("::") {
1015 Some((tool_name, lint_name)) => {
1016 let tool_name = Symbol::intern(tool_name);
1017
1018 (Some(tool_name), lint_name)
1019 }
1020 None => (None, lint_name),
1021 }
1022}