Rollup merge of #147136 - Jules-Bertholet:const-_-unused-vis, r=jdonszelmann

Add warn-by-default lint for visibility on `const _` declarations

Add a warn-by-default `unused_visibilities` lint for visibility qualifiers on `const _` declarations—e.g. `pub const _: () = ();`. Such qualifiers have no effect.

A [Sourcegraph search](https://sourcegraph.com/search?q=context:global+lang:Rust+pub%5Cs*%28%5C%28.*%5C%29%29%3F%5Cs*const%5Cs%2B_%5Cs*:&patternType=regexp&case=yes&sm=0) suggests that this pattern is relatively rare, and mostly found in tests (with only 3 exceptions). So perhaps this could become an FCW/hard error in the future.

`@rustbot` label T-lang A-lints A-visibility -T-clippy
This commit is contained in:
Matthias Krüger 2025-12-07 08:26:49 +01:00 committed by GitHub
commit 842f95de70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 193 additions and 14 deletions

View file

@ -33,7 +33,7 @@ use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY,
PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES,
};
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, kw, sym};
@ -1339,7 +1339,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
});
}
ItemKind::Const(box ConstItem { defaultness, rhs, .. }) => {
ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => {
self.check_defaultness(item.span, *defaultness);
if rhs.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
@ -1347,6 +1347,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
replace_span: self.ending_semi_or_hi(item.span),
});
}
if ident.name == kw::Underscore
&& !matches!(item.vis.kind, VisibilityKind::Inherited)
&& ident.span.eq_ctxt(item.vis.span)
{
self.lint_buffer.buffer_lint(
UNUSED_VISIBILITIES,
item.id,
item.vis.span,
BuiltinLintDiag::UnusedVisibility(item.vis.span),
)
}
visit::walk_item(self, item);
}
ItemKind::Static(box StaticItem { expr, safety, .. }) => {

View file

@ -978,6 +978,10 @@ lint_unused_op = unused {$op} that must be used
lint_unused_result = unused result of type `{$ty}`
lint_unused_visibilities = visibility qualifiers have no effect on `const _` declarations
.note = `const _` does not declare a name, so there is nothing for the qualifier to apply to
.suggestion = remove the qualifier
lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result
lint_useless_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false

View file

@ -302,6 +302,9 @@ pub fn decorate_builtin_lint(
BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
}
BuiltinLintDiag::UnusedVisibility(span) => {
lints::UnusedVisibility { span }.decorate_lint(diag)
}
BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag),
}
}

View file

@ -291,6 +291,7 @@ fn register_builtins(store: &mut LintStore) {
"unused",
UNUSED_IMPORTS,
UNUSED_VARIABLES,
UNUSED_VISIBILITIES,
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNUSED_MUT,

View file

@ -3191,3 +3191,11 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(LintDiagnostic)]
#[diag(lint_unused_visibilities)]
#[note]
pub(crate) struct UnusedVisibility {
#[suggestion(style = "short", code = "", applicability = "machine-applicable")]
pub span: Span,
}

View file

@ -143,6 +143,7 @@ declare_lint_pass! {
UNUSED_QUALIFICATIONS,
UNUSED_UNSAFE,
UNUSED_VARIABLES,
UNUSED_VISIBILITIES,
USELESS_DEPRECATED,
VARARGS_WITHOUT_PATTERN,
WARNINGS,
@ -693,6 +694,26 @@ declare_lint! {
"detect variables which are not used in any way"
}
declare_lint! {
/// The `unused_visibilities` lint detects visibility qualifiers (like `pub`)
/// on a `const _` item.
///
/// ### Example
///
/// ```rust
/// pub const _: () = {};
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// These qualifiers have no effect, as `const _` items are unnameable.
pub UNUSED_VISIBILITIES,
Warn,
"detect visibility qualifiers on `const _` items"
}
declare_lint! {
/// The `unused_assignments` lint detects assignments that will never be read.
///

View file

@ -696,6 +696,7 @@ pub enum BuiltinLintDiag {
extern_crate: Symbol,
local_crate: Symbol,
},
UnusedVisibility(Span),
AttributeLint(AttributeLintKind),
}

View file

@ -71,10 +71,10 @@ LL | impl<T: core::cmp::Eq> core::fmt::Display for X<T>
| ^^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:113:14
--> tests/ui-toml/absolute_paths/absolute_paths.rs:113:10
|
LL | pub const _: crate::S = {
| ^^^^^^^^
LL | const _: crate::S = {
| ^^^^^^^^
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:114:9

View file

@ -110,7 +110,7 @@ mod m1 {
}
//~[no_short]v absolute_paths
pub const _: crate::S = {
const _: crate::S = {
let crate::S = m1::S; //~[no_short] absolute_paths
crate::m1::S

View file

@ -4,12 +4,12 @@ pub const C: () = {
//~^ ERROR: destructor of `String` cannot be evaluated at compile-time
};
pub const _: () = {
const _: () = {
let _: &'static _ = &id(&String::new());
//~^ ERROR: destructor of `String` cannot be evaluated at compile-time
};
pub const _: () = {
const _: () = {
let _: &'static _ = &std::mem::ManuallyDrop::new(String::new());
//~^ ERROR: temporary value dropped while borrowed
};

View file

@ -5,12 +5,12 @@
const FOO: &usize = &42;
pub const _: () = assert!(!(FOO as *const usize).is_null());
const _: () = assert!(!(FOO as *const usize).is_null());
pub const _: () = assert!(!(42 as *const usize).is_null());
const _: () = assert!(!(42 as *const usize).is_null());
pub const _: () = assert!((0 as *const usize).is_null());
const _: () = assert!((0 as *const usize).is_null());
pub const _: () = assert!(std::ptr::null::<usize>().is_null());
const _: () = assert!(std::ptr::null::<usize>().is_null());
pub const _: () = assert!(!("foo" as *const str).is_null());
const _: () = assert!(!("foo" as *const str).is_null());

View file

@ -413,3 +413,21 @@ note: the lint level is defined here
LL | #![forbid(forbidden_lint_groups)]
| ^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: warn(unused) incompatible with previous forbid
--> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:22:13
|
LL | #![forbid(unused)]
| ------ `forbid` level set here
LL | #![deny(unused)]
LL | #![warn(unused)]
| ^^^^^^ overruled by previous forbid
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #81670 <https://github.com/rust-lang/rust/issues/81670>
note: the lint level is defined here
--> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:17:11
|
LL | #![forbid(forbidden_lint_groups)]
| ^^^^^^^^^^^^^^^^^^^^^

View file

@ -453,3 +453,21 @@ note: the lint level is defined here
LL | #![forbid(forbidden_lint_groups)]
| ^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: allow(unused) incompatible with previous forbid
--> $DIR/outer-forbid.rs:25:9
|
LL | #![forbid(unused, non_snake_case)]
| ------ `forbid` level set here
...
LL | #[allow(unused)]
| ^^^^^^ overruled by previous forbid
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #81670 <https://github.com/rust-lang/rust/issues/81670>
note: the lint level is defined here
--> $DIR/outer-forbid.rs:18:11
|
LL | #![forbid(forbidden_lint_groups)]
| ^^^^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,29 @@
//@ check-pass
//@ run-rustfix
#![warn(unused_visibilities)]
const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
macro_rules! foo {
() => {
const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
};
}
foo!();
macro_rules! bar {
($tt:tt) => {
pub const $tt: () = {};
};
}
bar!(_);
fn main() {}

View file

@ -0,0 +1,29 @@
//@ check-pass
//@ run-rustfix
#![warn(unused_visibilities)]
pub const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
pub(self) const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
macro_rules! foo {
() => {
pub const _: () = {};
//~^WARN visibility qualifiers have no effect on `const _` declarations
};
}
foo!();
macro_rules! bar {
($tt:tt) => {
pub const $tt: () = {};
};
}
bar!(_);
fn main() {}

View file

@ -0,0 +1,35 @@
warning: visibility qualifiers have no effect on `const _` declarations
--> $DIR/unused-visibilities.rs:6:1
|
LL | pub const _: () = {};
| ^^^ help: remove the qualifier
|
= note: `const _` does not declare a name, so there is nothing for the qualifier to apply to
note: the lint level is defined here
--> $DIR/unused-visibilities.rs:4:9
|
LL | #![warn(unused_visibilities)]
| ^^^^^^^^^^^^^^^^^^^
warning: visibility qualifiers have no effect on `const _` declarations
--> $DIR/unused-visibilities.rs:9:1
|
LL | pub(self) const _: () = {};
| ^^^^^^^^^ help: remove the qualifier
|
= note: `const _` does not declare a name, so there is nothing for the qualifier to apply to
warning: visibility qualifiers have no effect on `const _` declarations
--> $DIR/unused-visibilities.rs:14:9
|
LL | pub const _: () = {};
| ^^^ help: remove the qualifier
...
LL | foo!();
| ------ in this macro invocation
|
= note: `const _` does not declare a name, so there is nothing for the qualifier to apply to
= note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: 3 warnings emitted

View file

@ -3,7 +3,7 @@
#![feature(const_closures, const_trait_impl)]
#![allow(incomplete_features)]
pub const _: () = {
const _: () = {
assert!((const || true)());
//~^ ERROR }: [const] Fn()` is not satisfied
};