1 use crate::context::{CheckLintNameResult, LintStore};
2 use crate::late::unerased_lint_store;
4 DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
5 RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
8 use rustc_ast_pretty::pprust;
9 use rustc_data_structures::fx::FxHashMap;
10 use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
12 use rustc_hir::intravisit::{self, Visitor};
14 use rustc_index::vec::IndexVec;
15 use rustc_middle::hir::nested_filter;
16 use rustc_middle::lint::{
17 reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
20 use rustc_middle::ty::query::Providers;
21 use rustc_middle::ty::{RegisteredTools, TyCtxt};
22 use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
23 use rustc_session::lint::{
24 builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
25 Level, Lint, LintExpectationId, LintId,
27 use rustc_session::parse::{add_feature_diagnostics, feature_err};
28 use rustc_session::Session;
29 use rustc_span::symbol::{sym, Symbol};
30 use rustc_span::{Span, DUMMY_SP};
33 MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
34 UnknownToolInScopedLint,
37 /// Collection of lint levels for the whole crate.
38 /// This is used by AST-based lints, which do not
39 /// wait until we have built HIR to be emitted.
41 struct LintLevelSets {
42 /// Linked list of specifications.
43 list: IndexVec<LintStackIndex, LintSet>,
46 rustc_index::newtype_index! {
47 #[custom_encodable] // we don't need encoding
48 struct LintStackIndex {
49 const COMMAND_LINE = 0;
53 /// Specifications found at this position in the stack. This map only represents the lints
54 /// found for one set of attributes (like `shallow_lint_levels_on` does).
56 /// We store the level specifications as a linked list.
57 /// Each `LintSet` represents a set of attributes on the same AST node.
58 /// The `parent` forms a linked list that matches the AST tree.
59 /// This way, walking the linked list is equivalent to walking the AST bottom-up
60 /// to find the specifications for a given lint.
63 // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
65 specs: FxHashMap<LintId, LevelAndSource>,
66 parent: LintStackIndex,
71 LintLevelSets { list: IndexVec::new() }
78 aux: Option<&FxHashMap<LintId, LevelAndSource>>,
81 let lint = LintId::of(lint);
82 let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
83 let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
84 self.raw_lint_id_level(id, idx, aux)
92 mut idx: LintStackIndex,
93 aux: Option<&FxHashMap<LintId, LevelAndSource>>,
94 ) -> (Option<Level>, LintLevelSource) {
95 if let Some(specs) = aux {
96 if let Some(&(level, src)) = specs.get(&id) {
97 return (Some(level), src);
101 let LintSet { ref specs, parent } = self.list[idx];
102 if let Some(&(level, src)) = specs.get(&id) {
103 return (Some(level), src);
105 if idx == COMMAND_LINE {
106 return (None, LintLevelSource::Default);
113 fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
114 let store = unerased_lint_store(tcx);
116 let mut builder = LintLevelsBuilder {
118 provider: QueryMapExpectationsWrapper {
120 cur: hir::CRATE_HIR_ID,
121 specs: ShallowLintLevelMap::default(),
122 expectations: Vec::new(),
123 unstable_to_stable_ids: FxHashMap::default(),
124 empty: FxHashMap::default(),
126 warn_about_weird_lints: false,
128 registered_tools: &tcx.resolutions(()).registered_tools,
131 builder.add_command_line();
132 builder.add_id(hir::CRATE_HIR_ID);
133 tcx.hir().walk_toplevel_module(&mut builder);
135 tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
137 builder.provider.expectations
140 #[instrument(level = "trace", skip(tcx), ret)]
141 fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
142 let store = unerased_lint_store(tcx);
143 let attrs = tcx.hir_attrs(owner);
145 let mut levels = LintLevelsBuilder {
147 provider: LintLevelQueryMap {
150 specs: ShallowLintLevelMap::default(),
151 empty: FxHashMap::default(),
154 warn_about_weird_lints: false,
156 registered_tools: &tcx.resolutions(()).registered_tools,
159 if owner == hir::CRATE_OWNER_ID {
160 levels.add_command_line();
163 match attrs.map.range(..) {
164 // There is only something to do if there are attributes at all.
166 // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
167 [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
168 // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
170 // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's.
171 _ => match tcx.hir().owner(owner) {
172 hir::OwnerNode::Item(item) => levels.visit_item(item),
173 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
174 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
175 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
176 hir::OwnerNode::Crate(mod_) => {
177 levels.add_id(hir::CRATE_HIR_ID);
178 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
183 let specs = levels.provider.specs;
185 #[cfg(debug_assertions)]
186 for (_, v) in specs.specs.iter() {
187 debug_assert!(!v.is_empty());
198 pub trait LintLevelsProvider {
199 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>;
200 fn insert(&mut self, id: LintId, lvl: LevelAndSource);
201 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
202 fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {}
205 impl LintLevelsProvider for TopDown {
206 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
207 &self.sets.list[self.cur].specs
210 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
211 self.sets.list[self.cur].specs.insert(id, lvl);
214 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
215 self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
219 struct LintLevelQueryMap<'tcx> {
222 specs: ShallowLintLevelMap,
223 /// Empty hash map to simplify code.
224 empty: FxHashMap<LintId, LevelAndSource>,
225 attrs: &'tcx hir::AttributeMap<'tcx>,
228 impl LintLevelsProvider for LintLevelQueryMap<'_> {
229 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
230 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
232 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
233 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
235 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
236 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
240 struct QueryMapExpectationsWrapper<'tcx> {
243 specs: ShallowLintLevelMap,
244 expectations: Vec<(LintExpectationId, LintExpectation)>,
245 unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
246 /// Empty hash map to simplify code.
247 empty: FxHashMap<LintId, LevelAndSource>,
250 impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
251 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
252 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
254 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
255 let specs = self.specs.specs.get_mut_or_insert_default(self.cur.local_id);
257 specs.insert(id, lvl);
259 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
260 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
262 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
263 let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
264 let key = LintExpectationId::Unstable { attr_id, lint_index: None };
266 if !self.unstable_to_stable_ids.contains_key(&key) {
267 self.unstable_to_stable_ids.insert(
269 LintExpectationId::Stable { hir_id, attr_index, lint_index: None, attr_id: None },
273 self.expectations.push((id.normalize(), expectation));
277 impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
278 fn add_id(&mut self, hir_id: HirId) {
279 self.provider.cur = hir_id;
281 self.provider.attrs.get(hir_id.local_id),
282 hir_id == hir::CRATE_HIR_ID,
288 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
289 type NestedFilter = nested_filter::OnlyBodies;
291 fn nested_visit_map(&mut self) -> Self::Map {
292 self.provider.tcx.hir()
295 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
296 self.add_id(param.hir_id);
297 intravisit::walk_param(self, param);
300 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
301 self.add_id(it.hir_id());
302 intravisit::walk_item(self, it);
305 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
306 self.add_id(it.hir_id());
307 intravisit::walk_foreign_item(self, it);
310 fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
311 // We will call `add_id` when we walk
312 // the `StmtKind`. The outer statement itself doesn't
313 // define the lint levels.
314 intravisit::walk_stmt(self, e);
317 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
318 self.add_id(e.hir_id);
319 intravisit::walk_expr(self, e);
322 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
323 self.add_id(s.hir_id);
324 intravisit::walk_field_def(self, s);
327 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
328 self.add_id(v.hir_id);
329 intravisit::walk_variant(self, v);
332 fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
333 self.add_id(l.hir_id);
334 intravisit::walk_local(self, l);
337 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
338 self.add_id(a.hir_id);
339 intravisit::walk_arm(self, a);
342 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
343 self.add_id(trait_item.hir_id());
344 intravisit::walk_trait_item(self, trait_item);
347 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
348 self.add_id(impl_item.hir_id());
349 intravisit::walk_impl_item(self, impl_item);
353 impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
354 fn add_id(&mut self, hir_id: HirId) {
355 self.provider.cur = hir_id;
356 self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
360 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
361 type NestedFilter = nested_filter::All;
363 fn nested_visit_map(&mut self) -> Self::Map {
364 self.provider.tcx.hir()
367 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
368 self.add_id(param.hir_id);
369 intravisit::walk_param(self, param);
372 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
373 self.add_id(it.hir_id());
374 intravisit::walk_item(self, it);
377 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
378 self.add_id(it.hir_id());
379 intravisit::walk_foreign_item(self, it);
382 fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
383 // We will call `add_id` when we walk
384 // the `StmtKind`. The outer statement itself doesn't
385 // define the lint levels.
386 intravisit::walk_stmt(self, e);
389 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
390 self.add_id(e.hir_id);
391 intravisit::walk_expr(self, e);
394 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
395 self.add_id(s.hir_id);
396 intravisit::walk_field_def(self, s);
399 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
400 self.add_id(v.hir_id);
401 intravisit::walk_variant(self, v);
404 fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
405 self.add_id(l.hir_id);
406 intravisit::walk_local(self, l);
409 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
410 self.add_id(a.hir_id);
411 intravisit::walk_arm(self, a);
414 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
415 self.add_id(trait_item.hir_id());
416 intravisit::walk_trait_item(self, trait_item);
419 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
420 self.add_id(impl_item.hir_id());
421 intravisit::walk_impl_item(self, impl_item);
425 pub struct LintLevelsBuilder<'s, P> {
428 warn_about_weird_lints: bool,
429 store: &'s LintStore,
430 registered_tools: &'s RegisteredTools,
433 pub(crate) struct BuilderPush {
434 prev: LintStackIndex,
437 impl<'s> LintLevelsBuilder<'s, TopDown> {
440 warn_about_weird_lints: bool,
441 store: &'s LintStore,
442 registered_tools: &'s RegisteredTools,
444 let mut builder = LintLevelsBuilder {
446 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
447 warn_about_weird_lints,
451 builder.process_command_line();
452 assert_eq!(builder.provider.sets.list.len(), 1);
456 fn process_command_line(&mut self) {
457 self.provider.cur = self
461 .push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
462 self.add_command_line();
465 /// Pushes a list of AST lint attributes onto this context.
467 /// This function will return a `BuilderPush` object which should be passed
468 /// to `pop` when this scope for the attributes provided is exited.
470 /// This function will perform a number of tasks:
472 /// * It'll validate all lint-related attributes in `attrs`
473 /// * It'll mark all lint-related attributes as used
474 /// * Lint levels will be updated based on the attributes provided
475 /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
478 /// Don't forget to call `pop`!
481 attrs: &[ast::Attribute],
483 source_hir_id: Option<HirId>,
485 let prev = self.provider.cur;
487 self.provider.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
489 self.add(attrs, is_crate_node, source_hir_id);
491 if self.provider.current_specs().is_empty() {
492 self.provider.sets.list.pop();
493 self.provider.cur = prev;
499 /// Called after `push` when the scope of a set of attributes are exited.
500 pub(crate) fn pop(&mut self, push: BuilderPush) {
501 self.provider.cur = push.prev;
502 std::mem::forget(push);
506 #[cfg(debug_assertions)]
507 impl Drop for BuilderPush {
509 panic!("Found a `push` without a `pop`.");
513 impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
514 pub(crate) fn sess(&self) -> &Session {
518 pub(crate) fn lint_store(&self) -> &LintStore {
522 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
523 self.provider.current_specs()
526 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
527 self.provider.insert(id, lvl)
530 fn add_command_line(&mut self) {
531 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
532 self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
533 let orig_level = level;
534 let lint_flag_val = Symbol::intern(lint_name);
536 let Ok(ids) = self.store.find_lints(&lint_name) else {
537 // errors handled in check_lint_name_cmdline above
541 // ForceWarn and Forbid cannot be overridden
542 if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
543 self.current_specs().get(&id)
548 if self.check_gated_lint(id, DUMMY_SP) {
549 let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
550 self.insert(id, (level, src));
556 /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
557 /// (e.g. if a forbid was already inserted on the same scope), then emits a
558 /// diagnostic with no change to `specs`.
559 fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
560 let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess);
561 if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id {
562 *id = id.normalize();
564 // Setting to a non-forbid level is an error if the lint previously had
565 // a forbid level. Note that this is not necessarily true even with a
566 // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
568 // This means that this only errors if we're truly lowering the lint
569 // level from forbid.
570 if level != Level::Forbid {
571 if let Level::Forbid = old_level {
572 // Backwards compatibility check:
574 // We used to not consider `forbid(lint_group)`
575 // as preventing `allow(lint)` for some lint `lint` in
576 // `lint_group`. For now, issue a future-compatibility
577 // warning for this case.
578 let id_name = id.lint.name_lower();
579 let fcw_warning = match old_src {
580 LintLevelSource::Default => false,
581 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
582 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
585 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
587 self.current_specs(),
591 let sub = match old_src {
592 LintLevelSource::Default => {
593 OverruledAttributeSub::DefaultSource { id: id.to_string() }
595 LintLevelSource::Node { span, reason, .. } => {
596 OverruledAttributeSub::NodeSource { span, reason }
598 LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
601 self.sess.emit_err(OverruledAttribute {
603 overruled: src.span(),
604 lint_level: level.as_str(),
605 lint_source: src.name(),
609 self.emit_spanned_lint(
610 FORBIDDEN_LINT_GROUPS,
612 OverruledAtributeLint {
613 overruled: src.span(),
614 lint_level: level.as_str(),
615 lint_source: src.name(),
621 // Retain the forbid lint level, unless we are
622 // issuing a FCW. In the FCW case, we want to
623 // respect the new setting.
630 // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
631 // Handling expectations of this lint would add additional complexity with little to no
632 // benefit. The expect level for this lint will therefore be ignored.
633 if let Level::Expect(_) = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) {
637 match (old_level, level) {
638 // If the new level is an expectation store it in `ForceWarn`
639 (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
640 self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
642 // Keep `ForceWarn` level but drop the expectation
643 (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
644 // Set the lint level as normal
645 _ => self.insert(id, (level, src)),
649 fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
650 let sess = self.sess;
651 for (attr_index, attr) in attrs.iter().enumerate() {
652 if attr.has_name(sym::automatically_derived) {
654 LintId::of(SINGLE_USE_LIFETIMES),
655 (Level::Allow, LintLevelSource::Default),
660 let level = match Level::from_attr(attr) {
662 // This is the only lint level with a `LintExpectationId` that can be created from an attribute
663 Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
664 let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id
665 else { bug!("stable id Level::from_attr") };
667 let stable_id = LintExpectationId::Stable {
669 attr_index: attr_index.try_into().unwrap(),
671 // we pass the previous unstable attr_id such that we can trace the ast id when building a map
672 // to go from unstable to stable id.
673 attr_id: Some(attr_id),
676 Level::Expect(stable_id)
681 let Some(mut metas) = attr.meta_item_list() else {
685 if metas.is_empty() {
686 // This emits the unused_attributes lint for `#[level()]`
690 // Before processing the lint names, look for a reason (RFC 2383)
692 let mut reason = None;
693 let tail_li = &metas[metas.len() - 1];
694 if let Some(item) = tail_li.meta_item() {
696 ast::MetaItemKind::Word => {} // actual lint names handled later
697 ast::MetaItemKind::NameValue(ref name_value) => {
698 if item.path == sym::reason {
699 if let ast::LitKind::Str(rationale, _) = name_value.kind {
700 if !self.sess.features_untracked().lint_reasons {
702 &self.sess.parse_sess,
705 "lint reasons are experimental",
709 reason = Some(rationale);
711 sess.emit_err(MalformedAttribute {
712 span: name_value.span,
713 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
718 // found reason, reslice meta list to exclude it
719 metas.pop().unwrap();
721 sess.emit_err(MalformedAttribute {
723 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
727 ast::MetaItemKind::List(_) => {
728 sess.emit_err(MalformedAttribute {
730 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
736 for (lint_index, li) in metas.iter_mut().enumerate() {
737 let level = match level {
738 Level::Expect(mut id) => {
739 id.set_lint_index(Some(lint_index as u16));
746 let meta_item = match li {
747 ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
749 if let Some(item) = li.meta_item() {
750 if let ast::MetaItemKind::NameValue(_) = item.kind {
751 if item.path == sym::reason {
752 sess.emit_err(MalformedAttribute {
754 sub: MalformedAttributeSub::ReasonMustComeLast(sp),
760 sess.emit_err(MalformedAttribute {
762 sub: MalformedAttributeSub::BadAttributeArgument(sp),
767 let tool_ident = if meta_item.path.segments.len() > 1 {
768 Some(meta_item.path.segments.remove(0).ident)
772 let tool_name = tool_ident.map(|ident| ident.name);
773 let name = pprust::path_to_string(&meta_item.path);
775 self.store.check_lint_name(&name, tool_name, self.registered_tools);
777 CheckLintNameResult::Ok(ids) => {
778 // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]`
779 // in that case we want to avoid overriding the lint level but instead add an expectation that
780 // can't be fulfilled. The lint message will include an explanation, that the
781 // `unfulfilled_lint_expectations` lint can't be expected.
782 if let Level::Expect(expect_id) = level {
783 // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we
784 // only need to check the slice if it contains a single lint.
785 let is_unfulfilled_lint_expectations = match ids {
786 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
789 self.provider.push_expectation(
791 LintExpectation::new(
794 is_unfulfilled_lint_expectations,
799 let src = LintLevelSource::Node {
804 .expect("empty lint name")
811 if self.check_gated_lint(id, attr.span) {
812 self.insert_spec(id, (level, src));
817 CheckLintNameResult::Tool(result) => {
821 &format!("{}::{}", tool_ident.unwrap().name, name);
822 let src = LintLevelSource::Node {
823 name: Symbol::intern(complete_name),
828 if self.check_gated_lint(id, attr.span) {
829 self.insert_spec(id, (level, src));
832 if let Level::Expect(expect_id) = level {
833 self.provider.push_expectation(
835 LintExpectation::new(reason, sp, false, tool_name),
839 Err((Some(ids), ref new_lint_name)) => {
840 let lint = builtin::RENAMED_AND_REMOVED_LINTS;
841 self.emit_spanned_lint(
847 replace: &new_lint_name,
851 let src = LintLevelSource::Node {
852 name: Symbol::intern(&new_lint_name),
857 self.insert_spec(*id, (level, src));
859 if let Level::Expect(expect_id) = level {
860 self.provider.push_expectation(
862 LintExpectation::new(reason, sp, false, tool_name),
867 // If Tool(Err(None, _)) is returned, then either the lint does not
868 // exist in the tool or the code was not compiled with the tool and
869 // therefore the lint was never added to the `LintStore`. To detect
870 // this is the responsibility of the lint tool.
875 &CheckLintNameResult::NoTool => {
876 sess.emit_err(UnknownToolInScopedLint {
877 span: tool_ident.map(|ident| ident.span),
878 tool_name: tool_name.unwrap(),
879 lint_name: pprust::path_to_string(&meta_item.path),
880 is_nightly_build: sess.is_nightly_build().then_some(()),
885 _ if !self.warn_about_weird_lints => {}
887 CheckLintNameResult::Warning(msg, renamed) => {
889 renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
891 replace: replace.as_str(),
893 self.emit_spanned_lint(
894 RENAMED_AND_REMOVED_LINTS,
896 RenamedOrRemovedLint { msg, suggestion },
899 CheckLintNameResult::NoLint(suggestion) => {
900 let name = if let Some(tool_ident) = tool_ident {
901 format!("{}::{}", tool_ident.name, name)
905 let suggestion = suggestion
906 .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
907 self.emit_spanned_lint(
910 UnknownLint { name, suggestion },
914 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
915 // This happens outside of the match because the new lint should be applied even if
916 // we don't warn about the name change.
917 if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
918 // Ignore any errors or warnings that happen because the new name is inaccurate
919 // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
920 if let CheckLintNameResult::Ok(ids) =
921 self.store.check_lint_name(&new_name, None, self.registered_tools)
923 let src = LintLevelSource::Node {
924 name: Symbol::intern(&new_name),
929 if self.check_gated_lint(id, attr.span) {
930 self.insert_spec(id, (level, src));
933 if let Level::Expect(expect_id) = level {
934 self.provider.push_expectation(
936 LintExpectation::new(reason, sp, false, tool_name),
940 panic!("renamed lint does not exist: {}", new_name);
947 for (id, &(level, ref src)) in self.current_specs().iter() {
948 if !id.lint.crate_level_only {
952 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src else {
956 self.emit_spanned_lint(
958 lint_attr_span.into(),
959 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
961 // don't set a separate error for every lint in the group
967 /// Checks if the lint is gated on a feature that is not enabled.
969 /// Returns `true` if the lint's feature is enabled.
970 // FIXME only emit this once for each attribute, instead of repeating it 4 times for
971 // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
972 fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
973 if let Some(feature) = lint_id.lint.feature_gate {
974 if !self.sess.features_untracked().enabled(feature) {
975 let lint = builtin::UNKNOWN_LINTS;
976 let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
983 fluent::lint_unknown_gated_lint,
985 lint.set_arg("name", lint_id.lint.name_lower());
986 lint.note(fluent::note);
987 add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
997 /// Find the lint level for a lint.
998 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
999 self.provider.get_lint_level(lint, self.sess)
1002 /// Used to emit a lint-related diagnostic based on the current state of
1003 /// this lint context.
1005 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
1007 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
1008 #[rustc_lint_diagnostics]
1009 pub(crate) fn struct_lint(
1011 lint: &'static Lint,
1012 span: Option<MultiSpan>,
1013 msg: impl Into<DiagnosticMessage>,
1014 decorate: impl for<'a, 'b> FnOnce(
1015 &'b mut DiagnosticBuilder<'a, ()>,
1016 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1018 let (level, src) = self.lint_level(lint);
1019 struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
1022 pub fn emit_spanned_lint(
1024 lint: &'static Lint,
1026 decorate: impl for<'a> DecorateLint<'a, ()>,
1028 let (level, src) = self.lint_level(lint);
1029 struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
1030 decorate.decorate_lint(lint)
1034 pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
1035 let (level, src) = self.lint_level(lint);
1036 struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
1037 decorate.decorate_lint(lint)
1042 pub(crate) fn provide(providers: &mut Providers) {
1043 *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };