From 5afdb7aa01dc54b09008f360ff00bc816b13c979 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:11:29 +0200 Subject: [PATCH 1/8] factor out lint name --- compiler/rustc_attr_ir/src/lint.rs | 75 ++++++++++++++---- .../rustc_attr_parsing/src/attributes/lint.rs | 45 ++++------- compiler/rustc_lint/src/diagnostics.rs | 2 +- compiler/rustc_lint/src/levels.rs | 50 ++++-------- compiler/rustc_passes/src/check_attr.rs | 77 ++++++++----------- .../clippy/clippy_lints/src/collapsible_if.rs | 10 ++- .../src/returns/needless_return.rs | 9 ++- tests/pretty/delegation/generics.pp | 2 +- tests/pretty/delegation/inherit-attributes.pp | 2 +- tests/pretty/delegation/inline-attribute.pp | 2 +- tests/pretty/hir-delegation.pp | 2 +- tests/pretty/hir-lifetimes.pp | 3 +- tests/pretty/pin-ergonomics-hir.pp | 5 +- tests/ui/eii/cfg_on_eii.stdout | 3 +- tests/ui/unpretty/exhaustive.hir.stdout | 10 ++- ...ct-exprs-tuple-call-pretty-printing.stdout | 3 +- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 3 +- 17 files changed, 155 insertions(+), 148 deletions(-) diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index 4839412ed59ad..71fdb8a12d4d0 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,7 +1,12 @@ +use std::fmt; + +use rustc_ast as ast; +use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use crate::{HashIgnoredAttrId, PrintAttribute}; + #[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum LintCheckKind { Allow, @@ -43,21 +48,7 @@ impl LintCheckKind { /// ``` #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct LintCheck { - /// The lint's tool name, if present. - pub tool: Option, - /// The lint's name, minus the tool name if any. - /// - /// For example: - /// - `clippy::blah` -> `blah` - /// - `unstable_imports` -> `unstable_imports` - /// - `custom_tool::blah::bleh` -> `blah::bleh` - pub lint_name: Symbol, - /// The span of the lint name. - /// - /// For example `#[deny(foo, bar, reason = "reason")]` - /// produces multiple `LintCheck`s, one with a span pointing to `foo` - /// and another pointing to `bar`. - pub lint_span: Span, + pub lint_name: LintName, pub kind: LintCheckKind, pub reason: Option, /// Needed by `LintExpectationId` to track fulfilled expectations @@ -68,4 +59,56 @@ pub struct LintCheck { /// inside one attribute are all smooshed and flattened together, so /// multiple `LintCheck`s can have the same `attr_span`. pub attr_span: Span, + /// The span of the entire lint name. + /// + /// #[deny(foo::bar::baz)] + /// ^^^^^^^^^^^^^ + pub lint_span: Span, + pub tool_span: Option, +} + +#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub struct LintName { + /// The lint's tool name, if present. + pub tool: Option, + /// The lint's name, minus the tool name if any. + /// + /// For example: + /// - `clippy::blah` -> `blah` + /// - `unstable_imports` -> `unstable_imports` + /// - `custom_tool::blah::bleh` -> `blah::bleh` + pub name: Symbol, +} + +impl LintName { + pub fn new(path: &ast::Path) -> LintName { + let (tool, name) = match &*path.segments { + [] => unreachable!(), + [lint_name] => (None, lint_name.ident.name), + [tool, lint_name] => (Some(tool.ident.name), lint_name.ident.name), + // Not reachable for any builtin lints, + // but reachable using `register_tool`. + [tool, multi_segment_lint_name @ ..] => ( + Some(tool.ident.name), + Symbol::intern(&join_path_syms( + multi_segment_lint_name.iter().map(|seg| seg.ident.name), + )), + ), + }; + LintName { tool, name } + } + + pub fn is(&self, lint: Symbol) -> bool { + lint == self.name && self.tool.is_none() + } +} + +impl fmt::Display for LintName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(tool) = self.tool { + write!(f, "{tool}::")? + } + write!(f, "{}", self.name)?; + Ok(()) + } } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index db57b23f6296a..ae7e40b185f43 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -1,9 +1,8 @@ -use rustc_ast::join_path_syms; use rustc_attr_ir::AttributeKind; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; -use rustc_span::{Symbol, sym}; +use rustc_span::sym; use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; @@ -52,33 +51,19 @@ impl LintParser { match item { MetaItemOrLitParser::MetaItemParser(p) => { match p.args() { - ArgParser::NoArgs => { - let (tool, lint_name) = match &*p.path().0.segments { - [] => unreachable!(), - [lint_name] => (None, lint_name.ident.name), - [tool, lint_name] => (Some(tool.ident), lint_name.ident.name), - // Not reachable for any builtin lints, - // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => ( - Some(tool.ident), - Symbol::intern(&join_path_syms( - multi_segment_lint_name - .iter() - .map(|seg| seg.ident.name), - )), - ), - }; - - self.lints.push(LintCheck { - tool, - lint_name, - lint_span: p.span(), - kind, - attr_id, - reason, - attr_span, - }) - } + ArgParser::NoArgs => self.lints.push(LintCheck { + lint_name: LintName::new(&p.path().0), + kind, + attr_id, + reason, + attr_span, + lint_span: p.span(), + tool_span: if let [tool, _, ..] = &*p.path().0.segments { + Some(tool.span()) + } else { + None + }, + }), // We've found a `reason = "reason"` but we're not the last element. ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => { cx.emit_err(diagnostics::MalformedAttribute { diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 6273414e9ccda..8694495c0be5c 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -63,7 +63,7 @@ impl Subdiagnostic for OverruledAttributeSub { } #[derive(Diagnostic)] -#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)] +#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$lint_name}`", code = E0710)] pub(crate) struct UnknownToolInScopedLint { #[primary_span] pub span: Option, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a442c62819c8d..ad029e48b3737 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::CRATE_NODE_ID; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{Attribute, DocAttribute, find_attr}; use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; @@ -30,7 +30,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::CRATE_MOD_ID; -use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; @@ -728,10 +728,11 @@ where // FIXME: consider performing these checks in attribute parsing instead for (lint_index, lint_check) in lint_checks.into_iter().enumerate() { - let LintCheck { tool, lint_name, lint_span, kind, reason, attr_id, attr_span: _ } = + let LintCheck { lint_name, kind, reason, attr_id, attr_span: _, lint_span, tool_span } = lint_check; - let full_lint_name = lint_name.as_str(); + let full_lint_name = format!("{lint_name}"); + let LintName { tool, name: lint_name } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -746,21 +747,15 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = self.store.check_lint_name( - full_lint_name, - tool.map(|i| i.name), - self.registered_lint_tools, - ); + let lint_result = + self.store.check_lint_name(lint_name.as_str(), tool, self.registered_lint_tools); let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), CheckLintNameResult::Tool(ids, new_lint_name) => { let name = match new_lint_name { - None => { - let complete_name = &format!("{}::{lint_name}", tool.unwrap().name); - Symbol::intern(complete_name) - } + None => Symbol::intern(&full_lint_name), Some(new_lint_name) => { self.emit_span_lint( builtin::RENAMED_AND_REMOVED_LINTS, @@ -787,8 +782,8 @@ where CheckLintNameResult::NoTool => { sess.dcx().emit_err(UnknownToolInScopedLint { - span: Some(tool.unwrap().span), - tool_name: tool.unwrap().name, + span: Some(tool_span.unwrap()), + tool_name: tool.unwrap(), lint_name: full_lint_name.to_string(), is_nightly_build: sess.is_nightly_build(), }); @@ -799,13 +794,11 @@ where if self.lint_added_lints { let suggestion = RenamedLintSuggestion::WithSpan { suggestion: lint_span, replace }; - let name = tool - .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) - .unwrap_or_else(|| full_lint_name.to_string()); + self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RenamedLint { name: name.as_str(), replace, suggestion }, + RenamedLint { name: full_lint_name.as_str(), replace, suggestion }, ); } @@ -825,15 +818,10 @@ where CheckLintNameResult::Removed(ref reason) => { if self.lint_added_lints { - let name = if let Some(Ident { name, .. }) = tool { - &format!("{name}::{full_lint_name}") - } else { - full_lint_name - }; self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RemovedLint { name, reason }, + RemovedLint { name: &full_lint_name, reason }, ); } continue; @@ -841,9 +829,6 @@ where CheckLintNameResult::NoLint(suggestion) => { if self.lint_added_lints { - let name = tool - .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) - .unwrap_or_else(|| full_lint_name.to_string()); let suggestion = suggestion.map(|(replace, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: lint_span, @@ -854,7 +839,7 @@ where self.emit_span_lint( UNKNOWN_LINTS, lint_span.into(), - UnknownLint { name, suggestion }, + UnknownLint { name: full_lint_name, suggestion }, ); } continue; @@ -883,12 +868,7 @@ where }; self.provider.push_expectation( expect_id, - LintExpectation::new( - reason, - lint_span, - is_unfulfilled_lint_expectations, - tool.map(|i| i.name), - ), + LintExpectation::new(reason, lint_span, is_unfulfilled_lint_expectations, tool), ); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ab951a2cc1ce9..74783a9b8c164 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1412,54 +1412,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_lint_check(&self, hir_id: HirId, lints: &[LintCheck]) { - for lint in lints { - let LintCheck { - tool: None, - lint_name, - lint_span, - kind: _, - reason: _, - attr_id: _, - attr_span: _, - } = *lint - else { - continue; - }; - - match lint_name { - sym::dead_code_pub_in_binary => { - if !self.tcx.crate_types().contains(&CrateType::Executable) { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - lint_span, - diagnostics::Unused { - attr_span: lint_span, - note: diagnostics::UnusedNote::NoEffectDeadCodePubInBinary, - }, - ); - } + for LintCheck { lint_name, lint_span, .. } in lints { + if lint_name.is(sym::dead_code_pub_in_binary) { + if !self.tcx.crate_types().contains(&CrateType::Executable) { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *lint_span, + diagnostics::Unused { + attr_span: *lint_span, + note: diagnostics::UnusedNote::NoEffectDeadCodePubInBinary, + }, + ); } - sym::linker_messages | sym::linker_info => { - if hir_id == CRATE_HIR_ID - && self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)) - { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - lint_span, - diagnostics::Unused { - attr_span: lint_span, - note: diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly, - }, - ); - } + } + + if lint_name.is(sym::linker_messages) | lint_name.is(sym::linker_info) { + if hir_id == CRATE_HIR_ID + && self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)) + { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *lint_span, + diagnostics::Unused { + attr_span: *lint_span, + note: diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly, + }, + ); } - _ => {} } } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 54291d2867dec..c829b615221cd 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -10,7 +10,8 @@ use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; -use rustc_span::{BytePos, Ident, Span, Symbol}; +use rustc_span::{BytePos, Span, Symbol}; +use rustc_attr_ir::lint::LintName; declare_clippy_lint! { /// ### What it does @@ -247,13 +248,16 @@ impl CollapsibleIf { matches!( lint, LintCheck { - tool: Some(Ident { name: sym::clippy, .. }), + lint_name: LintName { + tool: Some(sym::clippy), + .. + }, kind: LintCheckKind::Expect, .. } ) }) - .any(|lint| [expected_lint_name, sym::style, sym::all].contains(&lint.lint_name)) => + .any(|lint| [expected_lint_name, sym::style, sym::all].contains(&lint.lint_name.name)) => { let attr_span = lints.first().unwrap().attr_span.to(lints.last().unwrap().attr_span); // There is an `expect` attribute -- check that there is no _other_ significant text diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 3e1b4be2cf031..7e9d659d2ce96 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -13,7 +13,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, HirId, MatchSource, StmtKind}; use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; -use rustc_span::{BytePos, Ident, Pos as _, Span}; +use rustc_span::{BytePos, Pos as _, Span}; +use rustc_attr_ir::lint::LintName; use std::borrow::Cow; use std::fmt::Display; @@ -188,8 +189,10 @@ fn check_final_expr<'tcx>( matches!( lint, LintCheck { - tool: Some(Ident { name: sym::clippy, .. }), - lint_name: sym::needless_return | sym::style | sym::all | sym::warnings, + lint_name: LintName { + tool: Some(sym::clippy), + name: sym::needless_return | sym::style | sym::all | sym::warnings + }, kind: LintCheckKind::Expect, .. } diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp index 5bfecd355396d..0baec4c4bde21 100644 --- a/tests/pretty/delegation/generics.pp +++ b/tests/pretty/delegation/generics.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index 575e309c392bb..b0f89d12cd169 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index dfe1097035674..753518972e357 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 15841d103e0f6..d2c7dc1d8bad4 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 92cc37b584b32..93151a3cd4679 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,4 +1,5 @@ -#![attr = LintCheck([LintCheck {lint_name: "unused", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "unused"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 5dd405c89258d..3b38ab005c608 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,6 +1,7 @@ #![attr = Feature([pin_ergonomics#0])] -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, -LintCheck {lint_name: "incomplete_features", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "incomplete_features"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index e49f3cdbb8c45..84b35b003a3d7 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -1,5 +1,6 @@ #![attr = Feature([extern_item_impls#0])] -#![attr = LintCheck([LintCheck {lint_name: "deprecated", kind: Deny}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "deprecated"}, +kind: Deny}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 7fb3f85b7f5ce..794f73a766f31 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -3,8 +3,9 @@ coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", -kind: Allow}, LintCheck {lint_name: "unused_attributes", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "unused_attributes"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use std::prelude::rust_2024::*; @@ -39,8 +40,9 @@ use self::prelude::*; * inner multi-line doc comment */ #[doc = "inner doc attribute"] -#[attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, -LintCheck {lint_name: "unused_variables", kind: Allow}])] +#[attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "unused_variables"}, +kind: Allow}])] #[attr = NoStd] mod attributes { diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index 11d31361acdd6..aacd7c313f294 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,6 +1,7 @@ #![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, gca_macroless_args#0])] -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 9c70b1fec7469..5eb05cf14fc2e 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -1,4 +1,5 @@ -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; From 123d20dd01c57e08028ab298adff0d3b117d4e2d Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:59:12 +0200 Subject: [PATCH 2/8] edit check_lint_name to accept LintName --- compiler/rustc_attr_ir/src/lint.rs | 35 +++++++++++++------ .../rustc_attr_parsing/src/attributes/lint.rs | 4 ++- compiler/rustc_lint/src/context.rs | 7 ++-- compiler/rustc_lint/src/levels.rs | 22 +++++++----- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index 71fdb8a12d4d0..a9d1ad351e36b 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,6 +1,5 @@ use std::fmt; -use rustc_ast as ast; use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::{Span, Symbol, sym}; @@ -81,23 +80,37 @@ pub struct LintName { } impl LintName { - pub fn new(path: &ast::Path) -> LintName { - let (tool, name) = match &*path.segments { + pub fn new(segments: impl Iterator) -> LintName { + let segments = segments.collect::>(); + let (tool, name) = match &*segments { [] => unreachable!(), - [lint_name] => (None, lint_name.ident.name), - [tool, lint_name] => (Some(tool.ident.name), lint_name.ident.name), + [lint_name] => (None, *lint_name), + [tool, lint_name] => (Some(*tool), *lint_name), // Not reachable for any builtin lints, // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => ( - Some(tool.ident.name), - Symbol::intern(&join_path_syms( - multi_segment_lint_name.iter().map(|seg| seg.ident.name), - )), - ), + [tool, multi_segment_lint_name @ ..] => { + (Some(*tool), Symbol::intern(&join_path_syms(multi_segment_lint_name))) + } }; LintName { tool, name } } + /// Panics if `lint` is empty. + pub fn from_str(lint: &str) -> LintName { + if lint.is_empty() { + if cfg!(debug_assertions) { + rustc_span::bug!("`lint` is empty") + } else { + return LintName { tool: None, name: sym::empty }; + } + } + if let Some((tool, name)) = lint.split_once("::") { + LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } + } else { + LintName { tool: None, name: Symbol::intern(lint) } + } + } + pub fn is(&self, lint: Symbol) -> bool { lint == self.name && self.tool.is_none() } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index ae7e40b185f43..1bc35ad1beca0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -52,7 +52,9 @@ impl LintParser { MetaItemOrLitParser::MetaItemParser(p) => { match p.args() { ArgParser::NoArgs => self.lints.push(LintCheck { - lint_name: LintName::new(&p.path().0), + lint_name: LintName::new( + p.path().0.segments.iter().map(|s| s.ident.name), + ), kind, attr_id, reason, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 9678ed65d2891..d85941df99cca 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -8,6 +8,7 @@ use std::slice; use rustc_abi as abi; use rustc_ast::BindingMode; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_ir::lint::LintName; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -342,10 +343,12 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - lint_name: &str, - tool_name: Option, + lint_name: &LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { + let LintName { tool: tool_name, name: primary_name } = *lint_name; + let lint_name = primary_name.as_str(); + if let Some(tool_name) = tool_name { // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. if tool_name != sym::rustc diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ad029e48b3737..5c57cd336dc5c 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -488,7 +488,9 @@ where .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - match self.store.check_lint_name(lint_name_only, tool_name, self.registered_lint_tools) + match self + .store + .check_lint_name(&LintName::from_str(lint_name), self.registered_lint_tools) { CheckLintNameResult::Renamed(ref replace) => { let name = lint_name.as_str(); @@ -732,7 +734,7 @@ where lint_check; let full_lint_name = format!("{lint_name}"); - let LintName { tool, name: lint_name } = lint_name; + let LintName { tool, name: primary_name } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -747,8 +749,9 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = - self.store.check_lint_name(lint_name.as_str(), tool, self.registered_lint_tools); + let lint_result = self.store.check_lint_name(&lint_name, self.registered_lint_tools); + + let lint_name = { primary_name }; let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), @@ -805,12 +808,13 @@ where // If this lint was renamed, apply the new lint instead of ignoring the // attribute. Ignore any errors or warnings that happen because the new // name is inaccurate. - // NOTE: `new_name` already includes the tool name, so we don't - // have to add it again. - let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(replace, None, self.registered_lint_tools) + let replace_check = self + .store + .check_lint_name(&LintName::from_str(replace), self.registered_lint_tools); + let (CheckLintNameResult::Ok(ids) | CheckLintNameResult::Tool(ids, _)) = + replace_check else { - panic!("renamed lint does not exist: {replace}"); + panic!("renamed lint does not exist: {replace} :{replace_check:?}"); }; (ids, Symbol::intern(&replace)) From 6a3df0995a73acbbbacd2328b837b5d155f20566 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:42:10 +0200 Subject: [PATCH 3/8] use Lintname in lint machinery --- compiler/rustc_attr_ir/src/lint.rs | 64 +---------------- compiler/rustc_attr_ir/src/pretty_printing.rs | 5 +- .../rustc_attr_parsing/src/attributes/lint.rs | 3 +- compiler/rustc_driver_impl/src/lib.rs | 14 ++-- compiler/rustc_lint/src/context.rs | 63 ++++++++-------- compiler/rustc_lint/src/levels.rs | 3 +- compiler/rustc_session/src/session.rs | 4 +- compiler/rustc_structures/src/lib.rs | 2 + compiler/rustc_structures/src/lint.rs | 71 +++++++++++++++++++ .../src/cargo/lint_groups_priority.rs | 9 +-- .../clippy/clippy_lints/src/collapsible_if.rs | 2 +- .../src/returns/needless_return.rs | 2 +- tests/pretty/delegation/generics.pp | 2 +- tests/pretty/delegation/inherit-attributes.pp | 2 +- tests/pretty/delegation/inline-attribute.pp | 2 +- tests/pretty/hir-delegation.pp | 2 +- tests/pretty/hir-lifetimes.pp | 2 +- tests/pretty/pin-ergonomics-hir.pp | 5 +- tests/ui/eii/cfg_on_eii.stdout | 2 +- tests/ui/unpretty/exhaustive.hir.stdout | 10 +-- ...ct-exprs-tuple-call-pretty-printing.stdout | 2 +- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 2 +- 22 files changed, 145 insertions(+), 128 deletions(-) create mode 100644 compiler/rustc_structures/src/lint.rs diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index a9d1ad351e36b..2dafadf4cb2a9 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,8 +1,6 @@ -use std::fmt; - -use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::{Span, Symbol, sym}; +use rustc_structures::LintName; use crate::{HashIgnoredAttrId, PrintAttribute}; @@ -65,63 +63,3 @@ pub struct LintCheck { pub lint_span: Span, pub tool_span: Option, } - -#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct LintName { - /// The lint's tool name, if present. - pub tool: Option, - /// The lint's name, minus the tool name if any. - /// - /// For example: - /// - `clippy::blah` -> `blah` - /// - `unstable_imports` -> `unstable_imports` - /// - `custom_tool::blah::bleh` -> `blah::bleh` - pub name: Symbol, -} - -impl LintName { - pub fn new(segments: impl Iterator) -> LintName { - let segments = segments.collect::>(); - let (tool, name) = match &*segments { - [] => unreachable!(), - [lint_name] => (None, *lint_name), - [tool, lint_name] => (Some(*tool), *lint_name), - // Not reachable for any builtin lints, - // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => { - (Some(*tool), Symbol::intern(&join_path_syms(multi_segment_lint_name))) - } - }; - LintName { tool, name } - } - - /// Panics if `lint` is empty. - pub fn from_str(lint: &str) -> LintName { - if lint.is_empty() { - if cfg!(debug_assertions) { - rustc_span::bug!("`lint` is empty") - } else { - return LintName { tool: None, name: sym::empty }; - } - } - if let Some((tool, name)) = lint.split_once("::") { - LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } - } else { - LintName { tool: None, name: Symbol::intern(lint) } - } - } - - pub fn is(&self, lint: Symbol) -> bool { - lint == self.name && self.tool.is_none() - } -} - -impl fmt::Display for LintName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(tool) = self.tool { - write!(f, "{tool}::")? - } - write!(f, "{}", self.name)?; - Ok(()) - } -} diff --git a/compiler/rustc_attr_ir/src/pretty_printing.rs b/compiler/rustc_attr_ir/src/pretty_printing.rs index 313602e48993f..4ffd0f07029a3 100644 --- a/compiler/rustc_attr_ir/src/pretty_printing.rs +++ b/compiler/rustc_attr_ir/src/pretty_printing.rs @@ -14,7 +14,9 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use rustc_structures::{CollapseMacroDebuginfo, CrateType, Limit, NativeLibKind, SanitizerSet}; +use rustc_structures::{ + CollapseMacroDebuginfo, CrateType, Limit, LintName, NativeLibKind, SanitizerSet, +}; use thin_vec::ThinVec; use crate::HashIgnoredAttrId; @@ -223,4 +225,5 @@ print_debug!( CrateType, NativeLibKind, CollapseMacroDebuginfo, + LintName, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index 1bc35ad1beca0..e0607c49124d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -1,8 +1,9 @@ use rustc_attr_ir::AttributeKind; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; use rustc_span::sym; +use rustc_structures::LintName; use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b878affae386a..33b019445686a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -59,7 +59,7 @@ use rustc_session::output::invalid_output_for_target; use rustc_session::{EarlyDiagCtxt, Session, config}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, FileName}; -use rustc_structures::CrateType; +use rustc_structures::{CrateType, LintName}; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use tracing::trace; @@ -990,11 +990,9 @@ Available lint options: lints } - fn sort_lint_groups( - lints: Vec<(&'static str, Vec, bool)>, - ) -> Vec<(&'static str, Vec)> { + fn sort_lint_groups(lints: Vec<(LintName, Vec, bool)>) -> Vec<(LintName, Vec)> { let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect(); - lints.sort_by_key(|l| l.0); + lints.sort_by(|left, right| Ord::cmp(&left.0, &right.0)); lints } @@ -1041,7 +1039,7 @@ Available lint options: loaded_groups .iter() .chain(&builtin_groups) - .map(|&(s, _)| s.chars().count()) + .map(|(s, _)| s.name.as_str().chars().count()) .max() .unwrap_or(0), ); @@ -1054,7 +1052,7 @@ Available lint options: safe_println!("Lint groups provided by rustc:\n"); - let print_lint_groups = |lints: Vec<(&'static str, Vec)>, all_warnings| { + let print_lint_groups = |lints: Vec<(LintName, Vec)>, all_warnings| { safe_println!(" {} sub-lints", padded("name")); safe_println!(" {} ---------", padded("----")); @@ -1063,7 +1061,7 @@ Available lint options: } for (name, to) in lints { - let name = name.to_lowercase().replace('_', "-"); + let name = name.to_string().to_lowercase().replace('_', "-"); let desc = to .into_iter() .map(|x| x.to_string().replace('_', "-")) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d85941df99cca..a0828074f2e6b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -8,7 +8,6 @@ use std::slice; use rustc_abi as abi; use rustc_ast::BindingMode; use rustc_ast::util::parser::ExprPrecedence; -use rustc_attr_ir::lint::LintName; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -33,6 +32,7 @@ use rustc_middle::ty::{ use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, bug, sym}; +use rustc_structures::LintName; use tracing::debug; use self::TargetLint::*; @@ -80,10 +80,10 @@ pub struct LintStore { pub(crate) late_lint_mod_passes: Vec, /// Lints indexed by name. - by_name: UnordMap, + by_name: UnordMap, /// Map of registered lint groups to what lints they expand to. - lint_groups: FxIndexMap<&'static str, LintGroup>, + lint_groups: FxIndexMap, } impl DynLintStore for LintStore { @@ -166,7 +166,7 @@ impl LintStore { &self.lints } - pub fn get_lint_groups(&self) -> impl Iterator, bool)> { + pub fn get_lint_groups(&self) -> impl Iterator, bool)> { self.lint_groups .iter() .filter(|(_, LintGroup { depr, .. })| { @@ -174,12 +174,12 @@ impl LintStore { depr.is_none() }) .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| { - (*k, lint_ids.clone(), *is_externally_loaded) + (k.clone(), lint_ids.clone(), *is_externally_loaded) }) } /// Returns all lint group names, including deprecated/aliased groups - pub fn get_all_group_names(&self) -> impl Iterator { + pub fn get_all_group_names(&self) -> impl Iterator { self.lint_groups.keys().copied() } @@ -209,14 +209,14 @@ impl LintStore { self.lints.push(lint); let id = LintId::of(lint); - if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { + if self.by_name.insert(LintName::from_str(&lint.name_lower()), Id(id)).is_some() { bug!("duplicate specification of lint {}", lint.name_lower()) } if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible { if let Some(edition) = reason.edition() { self.lint_groups - .entry(edition.lint_name()) + .entry(LintName::from_str(edition.lint_name())) .or_insert(LintGroup { lint_ids: vec![], is_externally_loaded: lint.is_externally_loaded, @@ -229,7 +229,7 @@ impl LintStore { // future version of rustc will cause existing code to stop compiling. // Lints tied to an edition don't count because they are opt-in. self.lint_groups - .entry("future_incompatible") + .entry(LintName::from_str("future_incompatible")) .or_insert(LintGroup { lint_ids: vec![], is_externally_loaded: lint.is_externally_loaded, @@ -243,14 +243,16 @@ impl LintStore { } fn insert_group(&mut self, name: &'static str, group: LintGroup) { - let previous = self.lint_groups.insert(name, group); + let previous = self.lint_groups.insert(LintName::from_str(name), group); if previous.is_some() { bug!("group {name:?} already exists"); } } pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) { - let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(group_name) else { + let Some(LintGroup { lint_ids, .. }) = + self.lint_groups.get(&LintName::from_str(group_name)) + else { bug!("group alias {alias:?} points to unregistered group {group_name:?}") }; @@ -289,7 +291,7 @@ impl LintStore { /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. #[track_caller] pub fn register_ignored(&mut self, name: &str) { - if self.by_name.insert(name.to_string(), Ignored).is_some() { + if self.by_name.insert(LintName::from_str(name), Ignored).is_some() { bug!("duplicate specification of lint {}", name); } } @@ -297,17 +299,18 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { - let Some(&Id(target)) = self.by_name.get(new_name) else { + let Some(&Id(target)) = self.by_name.get(&LintName::from_str(new_name)) else { bug!("invalid lint renaming of {} to {}", old_name, new_name); }; - self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); + self.by_name.insert(LintName::from_str(old_name), Renamed(new_name.to_string(), target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { - self.by_name.insert(name.into(), Removed(reason.into())); + self.by_name.insert(LintName::from_str(name), Removed(reason.into())); } pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { + let lint_name = &LintName::from_str(lint_name); match self.by_name.get(lint_name) { Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)), @@ -328,7 +331,7 @@ impl LintStore { self.lint_groups.keys().collect::>() ); let lint_name_str = lint_name.as_str(); - self.lint_groups.contains_key(lint_name_str) || { + self.lint_groups.contains_key(&LintName::from_str(lint_name_str)) || { let warnings_name_str = crate::WARNINGS.name_lower(); lint_name_str == warnings_name_str } @@ -346,8 +349,7 @@ impl LintStore { lint_name: &LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { - let LintName { tool: tool_name, name: primary_name } = *lint_name; - let lint_name = primary_name.as_str(); + let tool_name = lint_name.tool; if let Some(tool_name) = tool_name { // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. @@ -359,23 +361,19 @@ impl LintStore { } } - let complete_name = if let Some(tool_name) = tool_name { - &format!("{tool_name}::{lint_name}") - } else { - lint_name - }; + let complete_name: &str = &format!("{lint_name}"); + // If the lint was scoped with `tool::` check if the tool lint exists if let Some(tool_name) = tool_name { - match self.by_name.get(complete_name) { - None => match self.lint_groups.get(complete_name) { + match self.by_name.get(lint_name) { + None => match self.lint_groups.get(lint_name) { // If the lint isn't registered, there are two possibilities: None => { // 1. The tool is currently running, so this lint really doesn't exist. // FIXME: should this handle tools that never register a lint, like rustfmt? debug!("lints={:?}", self.by_name); - let tool_prefix = format!("{tool_name}::"); - return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { + return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) { self.no_lint_suggestion(complete_name, tool_name.as_str()) } else { // 2. The tool isn't currently running, so no lints will be registered. @@ -397,10 +395,10 @@ impl LintStore { _ => {} } } - match self.by_name.get(complete_name) { + match self.by_name.get(lint_name) { Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), - None => match self.lint_groups.get(complete_name) { + None => match self.lint_groups.get(lint_name) { // If neither the lint, nor the lint group exists check if there is a `clippy::` // variant of this lint None => self.check_tool_name_for_backwards_compat(complete_name, "clippy"), @@ -438,7 +436,7 @@ impl LintStore { .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k)) .collect(); groups.sort(); - let groups = groups.iter().map(|k| Symbol::intern(k)); + let groups = groups.iter().map(|k| Symbol::intern(&k.to_string())); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); let names: Vec = groups.chain(lints).collect(); let mut lookups = vec![Symbol::intern(&name_lower)]; @@ -460,8 +458,9 @@ impl LintStore { tool_name: &str, ) -> CheckLintNameResult<'_> { let complete_name = format!("{tool_name}::{lint_name}"); - match self.by_name.get(&complete_name) { - None => match self.lint_groups.get(&*complete_name) { + let nlint_name = LintName::from_str(&complete_name); + match self.by_name.get(&nlint_name) { + None => match self.lint_groups.get(&nlint_name) { // Now we are sure, that this lint exists nowhere None => self.no_lint_suggestion(lint_name, tool_name), Some(LintGroup { lint_ids, .. }) => { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 5c57cd336dc5c..ae5ef4d9e7784 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::CRATE_NODE_ID; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{Attribute, DocAttribute, find_attr}; use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; @@ -31,6 +31,7 @@ use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; +use rustc_structures::LintName; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ad866eb5b28a7..b69c26639e7e8 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -25,7 +25,7 @@ pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; -use rustc_structures::{CrateType, Limit}; +use rustc_structures::{CrateType, Limit, LintName}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ Arch, CfgAbi, CodeModel, DebuginfoKind, MergeFunctions, Os, PanicStrategy, RelocModel, @@ -596,7 +596,7 @@ impl CodegenUnits { } pub struct LintGroup { - pub name: &'static str, + pub name: LintName, pub lints: Vec, pub is_externally_loaded: bool, } diff --git a/compiler/rustc_structures/src/lib.rs b/compiler/rustc_structures/src/lib.rs index cb5dccdfcd180..caa3cdae45acc 100644 --- a/compiler/rustc_structures/src/lib.rs +++ b/compiler/rustc_structures/src/lib.rs @@ -5,12 +5,14 @@ mod collapse_macro_debug_info; mod crate_type; mod limit; +mod lint; mod native_lib_kind; mod sanitizer_set; pub use collapse_macro_debug_info::CollapseMacroDebuginfo; pub use crate_type::CrateType; pub use limit::Limit; +pub use lint::LintName; pub use native_lib_kind::NativeLibKind; pub use sanitizer_set::SanitizerSet; diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs new file mode 100644 index 0000000000000..d6891462f15a5 --- /dev/null +++ b/compiler/rustc_structures/src/lint.rs @@ -0,0 +1,71 @@ +use std::fmt; + +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable, StableHash}; +use rustc_span::{Symbol, sym}; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "nightly", derive(StableHash, Encodable, Decodable))] +pub struct LintName { + /// The lint's tool name, if present. + pub tool: Option, + /// The lint's name, minus the tool name if any. + /// + /// For example: + /// - `clippy::blah` -> `blah` + /// - `unstable_imports` -> `unstable_imports` + /// - `custom_tool::blah::bleh` -> `blah::bleh` + pub name: Symbol, +} + +impl LintName { + pub fn new(segments: impl Iterator) -> LintName { + let segments = segments.collect::>(); + let (tool, name) = match &*segments { + [] => unreachable!(), + [lint_name] => (None, *lint_name), + [tool, lint_name] => (Some(*tool), *lint_name), + // Not reachable for any builtin lints, + // but reachable using `register_tool`. + [tool, lint_name, rest @ ..] => { + let mut lint_name = String::from(lint_name.as_str()); + for seg in rest { + lint_name.push_str("::"); + lint_name.push_str(seg.as_str()); + } + (Some(*tool), Symbol::intern(&lint_name)) + } + }; + LintName { tool, name } + } + + /// Panics if `lint` is empty. + pub fn from_str(lint: &str) -> LintName { + if lint.is_empty() { + if cfg!(debug_assertions) { + rustc_span::bug!("`lint` is empty") + } else { + return LintName { tool: None, name: sym::empty }; + } + } + if let Some((tool, name)) = lint.split_once("::") { + LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } + } else { + LintName { tool: None, name: Symbol::intern(lint) } + } + } + + pub fn is(&self, lint: Symbol) -> bool { + lint == self.name && self.tool.is_none() + } +} + +impl fmt::Display for LintName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(tool) = self.tool { + write!(f, "{tool}::")? + } + write!(f, "{}", self.name)?; + Ok(()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index 3b35480297f66..dd87fe61b16ea 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos as _, SourceFile, Span, SyntaxContext}; +use rustc_structures::LintName; use std::ops::Range; use std::path::Path; use toml::Spanned; @@ -52,14 +53,14 @@ impl<'a> LintConfig<'a> { } } -fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { if name.get_ref() != "warnings" && let Some(config) = LintConfig::parse(config) { - if known_groups.contains(&**name.get_ref()) { + if known_groups.contains(&LintName::from_str(&**name.get_ref())) { groups.push((name, config)); } else { lints.push((name, config)); @@ -136,11 +137,11 @@ pub fn check(cx: &LateContext<'_>) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); for group in unerased_lint_store(cx.tcx.sess).get_all_group_names() { - match group.split_once("::") { + match group.tool { None => { rustc_groups.insert(group); }, - Some(("clippy", group)) => { + Some(rustc_span::sym::clippy) => { clippy_groups.insert(group); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index c829b615221cd..82ed4b1993f3b 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -11,7 +11,7 @@ use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_span::{BytePos, Span, Symbol}; -use rustc_attr_ir::lint::LintName; +use rustc_structures::LintName; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 7e9d659d2ce96..915211d74fc1b 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -14,7 +14,7 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, MatchSource, StmtKind}; use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; use rustc_span::{BytePos, Pos as _, Span}; -use rustc_attr_ir::lint::LintName; +use rustc_structures::LintName; use std::borrow::Cow; use std::fmt::Display; diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp index 0baec4c4bde21..f5b42b351633d 100644 --- a/tests/pretty/delegation/generics.pp +++ b/tests/pretty/delegation/generics.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index b0f89d12cd169..53f977ebbb7ac 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index 753518972e357..8787a9e167405 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index d2c7dc1d8bad4..97364da0e02d8 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 93151a3cd4679..daee41b37889d 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "unused"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "unused" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 3b38ab005c608..624cd37ce5fc4 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,6 +1,7 @@ #![attr = Feature([pin_ergonomics#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index 84b35b003a3d7..226ad1849fa43 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -1,5 +1,5 @@ #![attr = Feature([extern_item_impls#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "deprecated"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "deprecated" }, kind: Deny}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 794f73a766f31..b1c956c2fe9f1 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -3,8 +3,9 @@ coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "unused_attributes"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "unused_attributes" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] @@ -40,8 +41,9 @@ use self::prelude::*; * inner multi-line doc comment */ #[doc = "inner doc attribute"] -#[attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "unused_variables"}, +#[attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "unused_variables" }, kind: Allow}])] #[attr = NoStd] mod attributes { diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index aacd7c313f294..d94a76fb882e1 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,6 +1,6 @@ #![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, gca_macroless_args#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 5eb05cf14fc2e..d80b073e52b73 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] From f6f868345dc1aae2c85d34f80538a53ffdf0bc25 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:15:16 +0200 Subject: [PATCH 4/8] store Lintname in error types --- compiler/rustc_lint/src/context.rs | 88 ++++++----------- compiler/rustc_lint/src/diagnostics.rs | 93 +++++++++--------- compiler/rustc_lint/src/levels.rs | 96 ++++++++++--------- compiler/rustc_structures/src/lint.rs | 22 +++++ .../tests/ui/legacy_numeric_constants.fixed | 6 +- .../tests/ui/legacy_numeric_constants.rs | 6 +- 6 files changed, 154 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a0828074f2e6b..8a72c064f4823 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -101,7 +101,7 @@ enum TargetLint { Id(LintId), /// Temporary renaming, used for easing migration pain; see #16545 - Renamed(String, LintId), + Renamed(LintName, LintId), /// Lint with this name existed previously, but has been removed/deprecated. /// The string argument is the reason for removal. @@ -130,18 +130,18 @@ struct LintGroup { pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option<(Symbol, bool)>), + NoLint(Option<(LintName, bool)>), /// The lint refers to a tool that has not been registered. NoTool, /// The lint has been renamed to a new name. - Renamed(String), + Renamed(LintName), /// The lint has been removed due to the given reason. Removed(String), /// The lint is from a tool. The `LintId` will be returned as if it were a /// rustc lint. The `Option` indicates if the lint has been /// renamed. - Tool(&'a [LintId], Option), + Tool(&'a [LintId], Option), /// The lint is from a tool. Either the lint does not exist in the tool or /// the code was not compiled with the tool and therefore the lint was @@ -299,24 +299,24 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { - let Some(&Id(target)) = self.by_name.get(&LintName::from_str(new_name)) else { - bug!("invalid lint renaming of {} to {}", old_name, new_name); + let new_name = LintName::from_str(new_name); + let Some(&Id(target)) = self.by_name.get(&new_name) else { + bug!("invalid lint renaming of {old_name} to {new_name}"); }; - self.by_name.insert(LintName::from_str(old_name), Renamed(new_name.to_string(), target)); + self.by_name.insert(LintName::from_str(old_name), Renamed(new_name, target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { self.by_name.insert(LintName::from_str(name), Removed(reason.into())); } - pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { - let lint_name = &LintName::from_str(lint_name); - match self.by_name.get(lint_name) { + pub fn find_lints(&self, lint_name: LintName) -> Option<&[LintId]> { + match self.by_name.get(&lint_name) { Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)), Some(Removed(_)) => None, Some(Ignored) => Some(&[]), - None => match self.lint_groups.get(lint_name) { + None => match self.lint_groups.get(&lint_name) { Some(LintGroup { lint_ids, .. }) => Some(lint_ids), None => None, }, @@ -346,7 +346,7 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - lint_name: &LintName, + lint_name: LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { let tool_name = lint_name.tool; @@ -361,12 +361,10 @@ impl LintStore { } } - let complete_name: &str = &format!("{lint_name}"); - // If the lint was scoped with `tool::` check if the tool lint exists if let Some(tool_name) = tool_name { - match self.by_name.get(lint_name) { - None => match self.lint_groups.get(lint_name) { + match self.by_name.get(&lint_name) { + None => match self.lint_groups.get(&lint_name) { // If the lint isn't registered, there are two possibilities: None => { // 1. The tool is currently running, so this lint really doesn't exist. @@ -374,7 +372,7 @@ impl LintStore { debug!("lints={:?}", self.by_name); return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) { - self.no_lint_suggestion(complete_name, tool_name.as_str()) + self.no_lint_suggestion(lint_name) } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. @@ -383,7 +381,7 @@ impl LintStore { } Some(LintGroup { lint_ids, depr, .. }) => { return if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) } else { CheckLintNameResult::Tool(lint_ids, None) }; @@ -395,17 +393,15 @@ impl LintStore { _ => {} } } - match self.by_name.get(lint_name) { - Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), + match self.by_name.get(&lint_name) { + Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(*new_name), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), - None => match self.lint_groups.get(lint_name) { - // If neither the lint, nor the lint group exists check if there is a `clippy::` - // variant of this lint - None => self.check_tool_name_for_backwards_compat(complete_name, "clippy"), + None => match self.lint_groups.get(&lint_name) { + None => self.no_lint_suggestion(lint_name), Some(LintGroup { lint_ids, depr, .. }) => { // Check if the lint group name is deprecated if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) } else { CheckLintNameResult::Ok(lint_ids) } @@ -416,12 +412,12 @@ impl LintStore { } } - fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> { + fn no_lint_suggestion(&self, lint_name: LintName) -> CheckLintNameResult<'_> { let name_lower = lint_name.to_lowercase(); - if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_some() { + if self.find_lints(name_lower).is_some() { // First check if the lint name is (partly) in upper case instead of lower case... - return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false))); + return CheckLintNameResult::NoLint(Some((name_lower, false))); } // ...if not, search for lints with a similar name @@ -439,41 +435,17 @@ impl LintStore { let groups = groups.iter().map(|k| Symbol::intern(&k.to_string())); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); let names: Vec = groups.chain(lints).collect(); - let mut lookups = vec![Symbol::intern(&name_lower)]; - if let Some(stripped) = name_lower.split("::").last() { + let mut lookups = vec![Symbol::intern(&name_lower.to_string())]; + if let Some(stripped) = name_lower.name.as_str().split("::").last() { lookups.push(Symbol::intern(stripped)); } let res = find_best_match_for_names(&names, &lookups, None); - let is_rustc = res.map_or_else( - || false, - |s| name_lower.contains("::") && !s.as_str().starts_with(tool_name), - ); - let suggestion = res.map(|s| (s, is_rustc)); + let res = res.map(|s| LintName::from_str(s.as_str())); + let is_builtin = + res.as_ref().map(|s| lint_name.tool.is_some() && s.tool.is_none()).unwrap_or(false); + let suggestion = res.map(|s| (s, is_builtin)); CheckLintNameResult::NoLint(suggestion) } - - fn check_tool_name_for_backwards_compat( - &self, - lint_name: &str, - tool_name: &str, - ) -> CheckLintNameResult<'_> { - let complete_name = format!("{tool_name}::{lint_name}"); - let nlint_name = LintName::from_str(&complete_name); - match self.by_name.get(&nlint_name) { - None => match self.lint_groups.get(&nlint_name) { - // Now we are sure, that this lint exists nowhere - None => self.no_lint_suggestion(lint_name, tool_name), - Some(LintGroup { lint_ids, .. }) => { - CheckLintNameResult::Tool(lint_ids, Some(complete_name)) - } - }, - Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)), - Some(other) => { - debug!("got renamed lint {:?}", other); - CheckLintNameResult::NoLint(None) - } - } - } } /// Context for lint checking outside of type inference. diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 8694495c0be5c..f4e111ddc6760 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_structures::LintName; use crate::LateContext; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; @@ -68,7 +69,7 @@ pub(crate) struct UnknownToolInScopedLint { #[primary_span] pub span: Option, pub tool_name: Symbol, - pub lint_name: String, + pub lint_name: LintName, #[help("add `#![register_tool({$tool_name})]` to the crate root")] pub is_nightly_build: bool, } @@ -106,9 +107,9 @@ pub(crate) struct BuiltinEllipsisInclusiveRangePatterns { #[derive(Subdiagnostic)] #[note("requested on the command line with `{$level} {$lint_name}`")] -pub(crate) struct RequestedLevel<'a> { +pub(crate) struct RequestedLevel { pub level: rustc_lint_defs::Level, - pub lint_name: &'a str, + pub lint_name: LintName, } #[derive(Diagnostic)] @@ -119,10 +120,10 @@ pub(crate) struct UnsupportedGroup { #[derive(Diagnostic)] #[diag("unknown lint tool: `{$tool_name}`", code = E0602)] -pub(crate) struct CheckNameUnknownTool<'a> { +pub(crate) struct CheckNameUnknownTool { pub tool_name: Symbol, #[subdiagnostic] - pub sub: RequestedLevel<'a>, + pub sub: RequestedLevel, } // array_into_iter.rs @@ -1395,76 +1396,76 @@ pub(crate) struct OverruledAttributeLint<'a> { } #[derive(Diagnostic)] -#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -pub(crate) struct DeprecatedLintName<'a> { - pub name: String, - #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] +#[diag("lint name `{$lint_name}` is deprecated and may not have an effect in the future")] +pub(crate) struct DeprecatedLintName { + pub lint_name: LintName, + #[suggestion("change it to", code = "{replacement}", applicability = "machine-applicable")] pub suggestion: Span, - pub replace: &'a str, + pub replacement: LintName, } #[derive(Diagnostic)] -#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -#[help("change it to {$replace}")] -pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { - pub name: String, - pub replace: &'a str, +#[diag("lint name `{$lint_name}` is deprecated and may not have an effect in the future")] +#[help("change it to {$replacement}")] +pub(crate) struct DeprecatedLintNameFromCommandLine { + pub lint_name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLint<'a> { - pub name: &'a str, - pub replace: &'a str, +#[diag("lint `{$name}` has been renamed to `{$replacement}`")] +pub(crate) struct RenamedLint { + pub name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, } #[derive(Subdiagnostic)] -pub(crate) enum RenamedLintSuggestion<'a> { - #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] +pub(crate) enum RenamedLintSuggestion { + #[suggestion("use the new name", code = "{replacement}", applicability = "machine-applicable")] WithSpan { #[primary_span] suggestion: Span, - replace: &'a str, + replacement: LintName, }, - #[help("use the new name `{$replace}`")] - WithoutSpan { replace: &'a str }, + #[help("use the new name `{$replacement}`")] + WithoutSpan { replacement: LintName }, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLintFromCommandLine<'a> { - pub name: &'a str, - pub replace: &'a str, +#[diag("lint `{$lint_name}` has been renamed to `{$replacement}`")] +pub(crate) struct RenamedLintFromCommandLine { + pub lint_name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLint<'a> { - pub name: &'a str, + pub name: LintName, pub reason: &'a str, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been removed: {$reason}")] +#[diag("lint `{$lint_name}` has been removed: {$reason}")] pub(crate) struct RemovedLintFromCommandLine<'a> { - pub name: &'a str, + pub lint_name: LintName, pub reason: &'a str, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] #[diag("unknown lint: `{$name}`")] pub(crate) struct UnknownLint { - pub name: String, + pub name: LintName, #[subdiagnostic] pub suggestion: Option, } @@ -1476,32 +1477,32 @@ pub(crate) enum UnknownLintSuggestion { [true] a lint with a similar name exists in `rustc` lints *[false] did you mean }", - code = "{replace}", + code = "{replacement}", applicability = "maybe-incorrect" )] WithSpan { #[primary_span] suggestion: Span, - replace: Symbol, + replacement: LintName, from_rustc: bool, }, #[help( "{$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints: `{$replace}` - *[false] did you mean: `{$replace}` + [true] a lint with a similar name exists in `rustc` lints: `{$replacement}` + *[false] did you mean: `{$replacement}` }" )] - WithoutSpan { replace: Symbol, from_rustc: bool }, + WithoutSpan { replacement: LintName, from_rustc: bool }, } #[derive(Diagnostic)] -#[diag("unknown lint: `{$name}`", code = E0602)] -pub(crate) struct UnknownLintFromCommandLine<'a> { - pub name: String, +#[diag("unknown lint: `{$lint_name}`", code = E0602)] +pub(crate) struct UnknownLintFromCommandLine { + pub lint_name: LintName, #[subdiagnostic] pub suggestion: Option, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ae5ef4d9e7784..4e82620a9af21 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -481,45 +481,48 @@ where } fn add_command_line(&mut self) { - for &(ref lint_name, level) in &self.sess.opts.lint_opts { + for &(ref lint_name_, level) in &self.sess.opts.lint_opts { // Checks the validity of lint names derived from the command line. - let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); + let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name_); if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) { self.sess .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - match self - .store - .check_lint_name(&LintName::from_str(lint_name), self.registered_lint_tools) - { - CheckLintNameResult::Renamed(ref replace) => { - let name = lint_name.as_str(); - let suggestion = RenamedLintSuggestion::WithoutSpan { replace }; + let lint_name = LintName::from_str(lint_name_); + match self.store.check_lint_name(lint_name, self.registered_lint_tools) { + CheckLintNameResult::Renamed(replacement) => { + let suggestion = RenamedLintSuggestion::WithoutSpan { replacement }; let requested_level = RequestedLevel { level, lint_name }; - let lint = - RenamedLintFromCommandLine { name, replace, suggestion, requested_level }; + let lint = RenamedLintFromCommandLine { + lint_name, + replacement, + suggestion, + requested_level, + }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::Removed(ref reason) => { - let name = lint_name.as_str(); let requested_level = RequestedLevel { level, lint_name }; - let lint = RemovedLintFromCommandLine { name, reason, requested_level }; + let lint = RemovedLintFromCommandLine { lint_name, reason, requested_level }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::NoLint(suggestion) => { - let name = lint_name.clone(); - let suggestion = suggestion.map(|(replace, from_rustc)| { - UnknownLintSuggestion::WithoutSpan { replace, from_rustc } + let suggestion = suggestion.map(|(replacement, from_rustc)| { + UnknownLintSuggestion::WithoutSpan { replacement, from_rustc } }); let requested_level = RequestedLevel { level, lint_name }; - let lint = UnknownLintFromCommandLine { name, suggestion, requested_level }; + let lint = + UnknownLintFromCommandLine { lint_name, suggestion, requested_level }; self.emit_lint(UNKNOWN_LINTS, lint); } - CheckLintNameResult::Tool(_, Some(ref replace)) => { - let name = lint_name.clone(); + CheckLintNameResult::Tool(_, Some(replacement)) => { let requested_level = RequestedLevel { level, lint_name }; - let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level }; + let lint = DeprecatedLintNameFromCommandLine { + lint_name, + replacement, + requested_level, + }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::NoTool => { @@ -531,7 +534,7 @@ where _ => {} }; - let lint_flag_val = Symbol::intern(lint_name); + let lint_flag_val = Symbol::intern(lint_name_); let Some(ids) = self.store.find_lints(lint_name) else { // errors already handled above @@ -733,9 +736,7 @@ where for (lint_index, lint_check) in lint_checks.into_iter().enumerate() { let LintCheck { lint_name, kind, reason, attr_id, attr_span: _, lint_span, tool_span } = lint_check; - - let full_lint_name = format!("{lint_name}"); - let LintName { tool, name: primary_name } = lint_name; + let LintName { tool, name: _ } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -750,27 +751,25 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = self.store.check_lint_name(&lint_name, self.registered_lint_tools); - - let lint_name = { primary_name }; + let lint_result = self.store.check_lint_name(lint_name, self.registered_lint_tools); - let (ids, name) = match lint_result { + let (ids, name): (_, LintName) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), CheckLintNameResult::Tool(ids, new_lint_name) => { let name = match new_lint_name { - None => Symbol::intern(&full_lint_name), - Some(new_lint_name) => { + None => lint_name, + Some(replacement) => { self.emit_span_lint( builtin::RENAMED_AND_REMOVED_LINTS, lint_span.into(), DeprecatedLintName { - name: lint_name.to_string(), + lint_name, suggestion: lint_span, - replace: &new_lint_name, + replacement, }, ); - Symbol::intern(&new_lint_name) + replacement } }; (ids, name) @@ -788,37 +787,36 @@ where sess.dcx().emit_err(UnknownToolInScopedLint { span: Some(tool_span.unwrap()), tool_name: tool.unwrap(), - lint_name: full_lint_name.to_string(), + lint_name, is_nightly_build: sess.is_nightly_build(), }); continue; } - CheckLintNameResult::Renamed(ref replace) => { + CheckLintNameResult::Renamed(replacement) => { if self.lint_added_lints { let suggestion = - RenamedLintSuggestion::WithSpan { suggestion: lint_span, replace }; + RenamedLintSuggestion::WithSpan { suggestion: lint_span, replacement }; self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RenamedLint { name: full_lint_name.as_str(), replace, suggestion }, + RenamedLint { name: lint_name, replacement, suggestion }, ); } // If this lint was renamed, apply the new lint instead of ignoring the // attribute. Ignore any errors or warnings that happen because the new // name is inaccurate. - let replace_check = self - .store - .check_lint_name(&LintName::from_str(replace), self.registered_lint_tools); + let replace_check = + self.store.check_lint_name(replacement, self.registered_lint_tools); let (CheckLintNameResult::Ok(ids) | CheckLintNameResult::Tool(ids, _)) = replace_check else { - panic!("renamed lint does not exist: {replace} :{replace_check:?}"); + panic!("renamed lint does not exist: {replacement} :{replace_check:?}"); }; - (ids, Symbol::intern(&replace)) + (ids, replacement) } CheckLintNameResult::Removed(ref reason) => { @@ -826,7 +824,7 @@ where self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RemovedLint { name: &full_lint_name, reason }, + RemovedLint { name: lint_name, reason }, ); } continue; @@ -834,24 +832,28 @@ where CheckLintNameResult::NoLint(suggestion) => { if self.lint_added_lints { - let suggestion = suggestion.map(|(replace, from_rustc)| { + let suggestion = suggestion.map(|(replacement, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: lint_span, - replace, + replacement, from_rustc, } }); self.emit_span_lint( UNKNOWN_LINTS, lint_span.into(), - UnknownLint { name: full_lint_name, suggestion }, + UnknownLint { name: lint_name, suggestion }, ); } continue; } }; - let src = LintLevelSource::Node { name, span: lint_span, reason }; + let src = LintLevelSource::Node { + name: Symbol::intern(&name.to_string()), + span: lint_span, + reason, + }; for &id in ids { if self.check_gated_lint(id, lint_span, false) { self.insert_spec(id, LevelSpec::new(level, lint_id, src)); diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs index d6891462f15a5..9ae557ee2ff00 100644 --- a/compiler/rustc_structures/src/lint.rs +++ b/compiler/rustc_structures/src/lint.rs @@ -55,6 +55,18 @@ impl LintName { } } + pub fn to_lowercase(mut self) -> LintName { + fn lowercase(s: &mut Symbol) { + let lower = s.as_str().to_lowercase(); + *s = Symbol::intern(&lower); + } + if let Some(tool) = &mut self.tool { + lowercase(tool); + } + lowercase(&mut self.name); + self + } + pub fn is(&self, lint: Symbol) -> bool { lint == self.name && self.tool.is_none() } @@ -69,3 +81,13 @@ impl fmt::Display for LintName { Ok(()) } } + +#[cfg(feature = "nightly")] +impl rustc_error_messages::IntoDiagArg for LintName { + fn into_diag_arg( + self, + _: &mut Option, + ) -> rustc_error_messages::DiagArgValue { + self.to_string().into_diag_arg(&mut None) + } +} diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed index 98f26d62ab84c..762fc8422dca2 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed @@ -1,12 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::legacy_numeric_constants)] -#![expect(deprecated, renamed_and_removed_lints, clippy::no_effect, clippy::useless_attribute)] +#![expect(deprecated, clippy::no_effect, clippy::useless_attribute)] #[macro_use] extern crate proc_macros; pub mod a { - #[expect(legacy_numeric_constants)] + #[expect(clippy::legacy_numeric_constants)] pub use std::u128; } @@ -22,7 +22,7 @@ macro_rules! b { }; } -#[expect(legacy_numeric_constants)] +#[expect(clippy::legacy_numeric_constants)] use std::{f64, u8::MIN, u32, u32::MAX}; fn main() { diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs index 98f860130947f..5188f8a9de604 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs @@ -1,12 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::legacy_numeric_constants)] -#![expect(deprecated, renamed_and_removed_lints, clippy::no_effect, clippy::useless_attribute)] +#![expect(deprecated, clippy::no_effect, clippy::useless_attribute)] #[macro_use] extern crate proc_macros; pub mod a { - #[expect(legacy_numeric_constants)] + #[expect(clippy::legacy_numeric_constants)] pub use std::u128; } @@ -22,7 +22,7 @@ macro_rules! b { }; } -#[expect(legacy_numeric_constants)] +#[expect(clippy::legacy_numeric_constants)] use std::{f64, u8::MIN, u32, u32::MAX}; fn main() { From 09a506074c2e201150ddeffb1cd5ac3f86aba0d4 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:13:47 +0200 Subject: [PATCH 5/8] Use LintName in LintLevelSource --- compiler/rustc_lint/src/context.rs | 8 ++---- compiler/rustc_lint/src/diagnostics.rs | 22 ++++++++------- compiler/rustc_lint/src/levels.rs | 22 ++++++--------- compiler/rustc_middle/src/lint.rs | 34 +++++++++++------------- compiler/rustc_span/src/symbol.rs | 1 + src/tools/clippy/clippy_utils/src/sym.rs | 1 - 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8a72c064f4823..4ec3c5f7e97c4 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -324,17 +324,13 @@ impl LintStore { } /// True if this symbol represents a lint group name. - pub fn is_lint_group(&self, lint_name: Symbol) -> bool { + pub fn is_lint_group(&self, lint_name: LintName) -> bool { debug!( "is_lint_group(lint_name={:?}, lint_groups={:?})", lint_name, self.lint_groups.keys().collect::>() ); - let lint_name_str = lint_name.as_str(); - self.lint_groups.contains_key(&LintName::from_str(lint_name_str)) || { - let warnings_name_str = crate::WARNINGS.name_lower(); - lint_name_str == warnings_name_str - } + self.lint_groups.contains_key(&lint_name) || lint_name.is(sym::warnings) } /// Checks the name of a lint for its existence, and whether it was diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index f4e111ddc6760..459e1ffc11232 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -24,14 +24,14 @@ use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::lifetime_syntax::LifetimeSyntaxCategories; #[derive(Diagnostic)] -#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid", code = E0453)] +#[diag("{$lint_level}({$lint_name}) incompatible with previous forbid", code = E0453)] pub(crate) struct OverruledAttribute<'a> { #[primary_span] pub span: Span, #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, - pub lint_source: Symbol, + pub lint_name: LintName, #[subdiagnostic] pub sub: OverruledAttributeSub, } @@ -39,7 +39,7 @@ pub(crate) struct OverruledAttribute<'a> { pub(crate) enum OverruledAttributeSub { DefaultSource { id: String }, NodeSource { span: Span, reason: Option }, - CommandLineSource { id: Symbol }, + CommandLineSource { forbidden_lint_name: LintName }, } impl Subdiagnostic for OverruledAttributeSub { @@ -55,9 +55,11 @@ impl Subdiagnostic for OverruledAttributeSub { diag.note(rationale.to_string()); } } - OverruledAttributeSub::CommandLineSource { id } => { - diag.note(msg!("`forbid` lint level was set on command line (`-F {$id}`)")); - diag.arg("id", id); + OverruledAttributeSub::CommandLineSource { forbidden_lint_name } => { + diag.note(msg!( + "`forbid` lint level was set on command line (`-F {$forbidden_lint_name}`)" + )); + diag.arg("forbidden_lint_name", forbidden_lint_name); } } } @@ -1385,12 +1387,12 @@ impl Subdiagnostic for NonBindingLetSub { // levels.rs #[derive(Diagnostic)] -#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid")] +#[diag("{$lint_level}({$lint_name}) incompatible with previous forbid")] pub(crate) struct OverruledAttributeLint<'a> { #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, - pub lint_source: Symbol, + pub lint_name: LintName, #[subdiagnostic] pub sub: OverruledAttributeSub, } @@ -1506,10 +1508,10 @@ pub(crate) struct UnknownLintFromCommandLine { } #[derive(Diagnostic)] -#[diag("{$level}({$name}) is ignored unless specified at crate level")] +#[diag("{$level}({$lint_name}) is ignored unless specified at crate level")] pub(crate) struct IgnoredUnlessCrateSpecified<'a> { pub level: &'a str, - pub name: Symbol, + pub lint_name: LintName, } // dangling.rs diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4e82620a9af21..56a137df9ea75 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -534,8 +534,6 @@ where _ => {} }; - let lint_flag_val = Symbol::intern(lint_name_); - let Some(ids) = self.store.find_lints(lint_name) else { // errors already handled above continue; @@ -549,7 +547,7 @@ where } if self.check_gated_lint(id, DUMMY_SP, true) { - let src = LintLevelSource::CommandLine(lint_flag_val, level); + let src = LintLevelSource::CommandLine(lint_name, level); self.provider.insert(id, LevelSpec::new(level, None, src)); } } @@ -587,7 +585,7 @@ where let fcw_warning = match old_src { LintLevelSource::Default => false, LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), - LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), + LintLevelSource::CommandLine(name, _) => self.store.is_lint_group(name), }; debug!( "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", @@ -603,8 +601,8 @@ where LintLevelSource::Node { span, reason, .. } => { OverruledAttributeSub::NodeSource { span, reason } } - LintLevelSource::CommandLine(name, _) => { - OverruledAttributeSub::CommandLineSource { id: name } + LintLevelSource::CommandLine(lint_name, _) => { + OverruledAttributeSub::CommandLineSource { forbidden_lint_name: lint_name } } }; if !fcw_warning { @@ -612,7 +610,7 @@ where span: src.span(), overruled: src.span(), lint_level: level.as_str(), - lint_source: src.name(), + lint_name: src.name(), sub, }); } else { @@ -622,7 +620,7 @@ where OverruledAttributeLint { overruled: src.span(), lint_level: level.as_str(), - lint_source: src.name(), + lint_name: src.name(), sub, }, ); @@ -849,11 +847,7 @@ where } }; - let src = LintLevelSource::Node { - name: Symbol::intern(&name.to_string()), - span: lint_span, - reason, - }; + let src = LintLevelSource::Node { name, span: lint_span, reason }; for &id in ids { if self.check_gated_lint(id, lint_span, false) { self.insert_spec(id, LevelSpec::new(level, lint_id, src)); @@ -897,7 +891,7 @@ where lint_attr_span.into(), IgnoredUnlessCrateSpecified { level: level_spec.level().as_str(), - name: lint_attr_name, + lint_name: lint_attr_name, }, ); // don't set a separate error for every lint in the group diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index bef0fdf95d24e..94acf8e303a03 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -10,7 +10,8 @@ use rustc_lint_defs::{ }; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw, sym}; +use rustc_structures::LintName; use tracing::instrument; use crate::ty::TyCtxt; @@ -23,7 +24,7 @@ pub enum LintLevelSource { /// Lint level was set by an attribute. Node { - name: Symbol, + name: LintName, span: Span, /// RFC 2383 reason reason: Option, @@ -32,20 +33,20 @@ pub enum LintLevelSource { /// Lint level was set by a command-line flag. /// The provided `Level` is the level specified on the command line. /// (The actual level may be lower due to `--cap-lints`.) - CommandLine(Symbol, Level), + CommandLine(LintName, Level), } impl LintLevelSource { - pub fn name(&self) -> Symbol { - match *self { - LintLevelSource::Default => kw::Default, + pub fn name(self) -> LintName { + match self { + LintLevelSource::Default => LintName { tool: None, name: kw::Default }, LintLevelSource::Node { name, .. } => name, LintLevelSource::CommandLine(name, _) => name, } } - pub fn span(&self) -> Span { - match *self { + pub fn span(self) -> Span { + match self { LintLevelSource::Default => DUMMY_SP, LintLevelSource::Node { span, .. } => span, LintLevelSource::CommandLine(_, _) => DUMMY_SP, @@ -167,10 +168,7 @@ pub fn reveal_actual_level_spec( // See tests/ui/lint/rfc-2383-lint-reason/expect_warnings.rs. Level::Expect => true, Level::ForceWarn => { - sess.dcx().span_delayed_bug( - configured_level_spec.src.span(), - "cannot --force-warn the `warnings` lint group", - ); + sess.dcx().delayed_bug("cannot --force-warn the `warnings` lint group"); false } }; @@ -303,7 +301,7 @@ fn explain_lint_level_source( }) .map(|lint_group| lint_group.name) }; - let name = lint.name_lower(); + let name = LintName::from_str(&lint.name_lower()); if let Level::Allow = level { // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint // triggering. (#121009) @@ -323,18 +321,18 @@ fn explain_lint_level_source( } LintLevelSource::CommandLine(lint_flag_val, orig_level) => { let flag = orig_level.to_cmd_flag(); - let hyphen_case_lint_name = name.replace('_', "-"); - if lint_flag_val.as_str() == name { + let hyphen_case_lint_name = name.to_string().replace('_', "-"); + if lint_flag_val == name { err.note_once(format!( "requested on the command line with `{flag} {hyphen_case_lint_name}`" )); } else { - let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-"); + let hyphen_case_flag_val = lint_flag_val.to_string().replace('_', "-"); err.note_once(format!( "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); if matches!(orig_level, Level::Warn | Level::Deny) { - let help = if name == "dead_code" { + let help = if name.is(sym::dead_code) { format!( "to override `{flag} {hyphen_case_flag_val}` add `#[expect({name})]` or `#[allow({name})]`" ) @@ -352,7 +350,7 @@ fn explain_lint_level_source( err.note(rationale.to_string()); } err.span_note_once(span, "the lint level is defined here"); - if lint_attr_name.as_str() != name { + if lint_attr_name != name { let level_str = level.as_str(); err.note_once(format!( "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`" diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95197e6c98a9f..90e82be83c56e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2397,6 +2397,7 @@ symbols! { vtable_align, vtable_size, warn, + warnings, wasip2, wasm, wasm32, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 598841753c8af..fbdd3e2b7bee7 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -638,7 +638,6 @@ generate! { visit_str, visit_string, wake, - warnings, wildcard_imports, with_capacity, wrapping_neg, From a8a0f42f76bd603b9a53929b12bfe9499651d341 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 18 Oct 2026 22:31:30 +0200 Subject: [PATCH 6/8] Use LintName more --- compiler/rustc_lint/src/context.rs | 33 ++++++++++++++++-------------- compiler/rustc_lint/src/levels.rs | 21 +++++-------------- compiler/rustc_lint/src/tests.rs | 15 ++++++++------ 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 4ec3c5f7e97c4..654fee2f172dc 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -115,7 +115,7 @@ enum TargetLint { } struct LintAlias { - name: &'static str, + name: LintName, /// Whether deprecation warnings should be suppressed for this alias. silent: bool, } @@ -242,17 +242,17 @@ impl LintStore { } } - fn insert_group(&mut self, name: &'static str, group: LintGroup) { - let previous = self.lint_groups.insert(LintName::from_str(name), group); + fn insert_group(&mut self, name: LintName, group: LintGroup) { + let previous = self.lint_groups.insert(name, group); if previous.is_some() { - bug!("group {name:?} already exists"); + bug!("group {name} already exists"); } } - pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) { - let Some(LintGroup { lint_ids, .. }) = - self.lint_groups.get(&LintName::from_str(group_name)) - else { + pub fn register_group_alias(&mut self, group_name: &str, alias: &str) { + let group_name = LintName::from_str(group_name); + let alias = LintName::from_str(alias); + let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(&group_name) else { bug!("group alias {alias:?} points to unregistered group {group_name:?}") }; @@ -269,11 +269,13 @@ impl LintStore { pub fn register_group( &mut self, is_externally_loaded: bool, - name: &'static str, - deprecated_name: Option<&'static str>, + name: &str, + deprecated_name: Option<&str>, to: Vec, ) { + let name = LintName::from_str(name); if let Some(deprecated) = deprecated_name { + let deprecated = LintName::from_str(deprecated); self.insert_group( deprecated, LintGroup { @@ -299,11 +301,12 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { + let old_name = LintName::from_str(old_name); let new_name = LintName::from_str(new_name); let Some(&Id(target)) = self.by_name.get(&new_name) else { bug!("invalid lint renaming of {old_name} to {new_name}"); }; - self.by_name.insert(LintName::from_str(old_name), Renamed(new_name, target)); + self.by_name.insert(old_name, Renamed(new_name, target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { @@ -376,8 +379,8 @@ impl LintStore { }; } Some(LintGroup { lint_ids, depr, .. }) => { - return if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) + return if let Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(*name)) } else { CheckLintNameResult::Tool(lint_ids, None) }; @@ -396,8 +399,8 @@ impl LintStore { None => self.no_lint_suggestion(lint_name), Some(LintGroup { lint_ids, depr, .. }) => { // Check if the lint group name is deprecated - if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) + if let Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(*name)) } else { CheckLintNameResult::Ok(lint_ids) } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 56a137df9ea75..13487b6dd4c71 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -481,15 +481,15 @@ where } fn add_command_line(&mut self) { - for &(ref lint_name_, level) in &self.sess.opts.lint_opts { + for &(ref lint_name, level) in &self.sess.opts.lint_opts { + let lint_name = LintName::from_str(lint_name); // Checks the validity of lint names derived from the command line. - let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name_); - if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) { + if lint_name.is(sym::warnings) && matches!(level, Level::ForceWarn) { self.sess .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - let lint_name = LintName::from_str(lint_name_); + match self.store.check_lint_name(lint_name, self.registered_lint_tools) { CheckLintNameResult::Renamed(replacement) => { let suggestion = RenamedLintSuggestion::WithoutSpan { replacement }; @@ -527,7 +527,7 @@ where } CheckLintNameResult::NoTool => { self.sess.dcx().emit_err(CheckNameUnknownTool { - tool_name: tool_name.unwrap(), + tool_name: lint_name.tool.unwrap(), sub: RequestedLevel { level, lint_name }, }); } @@ -999,14 +999,3 @@ where pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { shallow_lint_levels_on, skippable_lints, ..*providers }; } - -pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option, &str) { - match lint_name.split_once("::") { - Some((tool_name, lint_name)) => { - let tool_name = Symbol::intern(tool_name); - - (Some(tool_name), lint_name) - } - None => (None, lint_name), - } -} diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index b3c91583914a1..b4f789564600d 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,21 +1,24 @@ #![allow(rustc::symbol_intern_string_literal)] -use rustc_span::{Symbol, create_default_session_globals_then}; +use rustc_span::{Symbol, create_default_session_globals_then, sym}; +use rustc_structures::LintName; use crate::builtin::is_hexagon_register_span; -use crate::levels::parse_lint_and_tool_name; #[test] fn parse_lint_no_tool() { create_default_session_globals_then(|| { - assert_eq!(parse_lint_and_tool_name("foo"), (None, "foo")) + assert_eq!(LintName::from_str("foo"), LintName { tool: None, name: Symbol::intern("foo") }) }); } #[test] fn parse_lint_with_tool() { create_default_session_globals_then(|| { - assert_eq!(parse_lint_and_tool_name("clippy::foo"), (Some(Symbol::intern("clippy")), "foo")) + assert_eq!( + LintName::from_str("clippy::foo"), + LintName { tool: Some(sym::clippy), name: Symbol::intern("foo") } + ) }); } @@ -23,8 +26,8 @@ fn parse_lint_with_tool() { fn parse_lint_multiple_path() { create_default_session_globals_then(|| { assert_eq!( - parse_lint_and_tool_name("clippy::foo::bar"), - (Some(Symbol::intern("clippy")), "foo::bar") + LintName::from_str("clippy::foo::bar"), + LintName { tool: Some(sym::clippy), name: Symbol::intern("foo::bar") } ) }); } From 232a750cc8fb16bc508a4f7a95434045073304c0 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:20:44 +0200 Subject: [PATCH 7/8] return tool_name in NoTool error --- compiler/rustc_lint/src/context.rs | 4 ++-- compiler/rustc_lint/src/levels.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 654fee2f172dc..01d121c1e7f73 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -132,7 +132,7 @@ pub enum CheckLintNameResult<'a> { /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. NoLint(Option<(LintName, bool)>), /// The lint refers to a tool that has not been registered. - NoTool, + NoTool(Symbol), /// The lint has been renamed to a new name. Renamed(LintName), /// The lint has been removed due to the given reason. @@ -356,7 +356,7 @@ impl LintStore { && tool_name != sym::rustdoc && !registered_lint_tools.contains(&Ident::with_dummy_span(tool_name)) { - return CheckLintNameResult::NoTool; + return CheckLintNameResult::NoTool(tool_name); } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 13487b6dd4c71..ededeb4196078 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -525,9 +525,9 @@ where }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } - CheckLintNameResult::NoTool => { + CheckLintNameResult::NoTool(tool_name) => { self.sess.dcx().emit_err(CheckNameUnknownTool { - tool_name: lint_name.tool.unwrap(), + tool_name, sub: RequestedLevel { level, lint_name }, }); } @@ -781,10 +781,10 @@ where continue; } - CheckLintNameResult::NoTool => { + CheckLintNameResult::NoTool(tool_name) => { sess.dcx().emit_err(UnknownToolInScopedLint { span: Some(tool_span.unwrap()), - tool_name: tool.unwrap(), + tool_name, lint_name, is_nightly_build: sess.is_nightly_build(), }); From 361557ad8780de87226c7c226aa42a0df283e05a Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 26 Oct 2026 23:39:59 +0100 Subject: [PATCH 8/8] clarify lint emptyness --- compiler/rustc_structures/src/lint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs index 9ae557ee2ff00..b76390c47ac4f 100644 --- a/compiler/rustc_structures/src/lint.rs +++ b/compiler/rustc_structures/src/lint.rs @@ -39,7 +39,7 @@ impl LintName { LintName { tool, name } } - /// Panics if `lint` is empty. + /// `lint` must not be empty. pub fn from_str(lint: &str) -> LintName { if lint.is_empty() { if cfg!(debug_assertions) {