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