Skip to content

Commit 4bc39f0

Browse files
committed
Auto merge of rust-lang#120924 - xFrednet:rfc-2383-stabilization-party, r=Urgau,blyxyas
Let's `#[expect]` some lints: Stabilize `lint_reasons` (RFC 2383) Let's give this another try! The [previous stabilization attempt](rust-lang#99063) was stalled by some unresolved questions. These have been discussed in a [lang team](rust-lang/lang-team#191) meeting. The last open question, regarding the semantics of the `#[expect]` attribute was decided on in rust-lang#115980 I've just updated the [stabilization report](rust-lang#54503 (comment)) with the discussed questions and decisions. Luckily, the decision is inline with the current implementation. This hopefully covers everything. Let's hope that the CI will be green like the spring. fixes rust-lang#115980 fixes rust-lang#54503 --- r? `@wesleywiser` Tacking Issue: rust-lang#54503 Stabilization Report: rust-lang#54503 (comment) Documentation Update: rust-lang/reference#1237 <!-- For Clippy: changelog: [`allow_attributes`]: Is now available on stable, since the `lint_reasons` feature was stabilized changelog: [`allow_attributes_without_reason`]: Is now available on stable, since the `lint_reasons` feature was stabilized --> --- Roses are red, Violets are blue, Let's expect lints, With reason clues
2 parents d7c5937 + 7666534 commit 4bc39f0

File tree

170 files changed

+908
-861
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+908
-861
lines changed

compiler/rustc_builtin_macros/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
#![allow(internal_features)]
66
#![allow(rustc::diagnostic_outside_of_impl)]
77
#![allow(rustc::untranslatable_diagnostic)]
8+
#![cfg_attr(bootstrap, feature(lint_reasons))]
89
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
910
#![doc(rust_logo)]
1011
#![feature(assert_matches)]
1112
#![feature(box_patterns)]
1213
#![feature(decl_macro)]
1314
#![feature(if_let_guard)]
1415
#![feature(let_chains)]
15-
#![feature(lint_reasons)]
1616
#![feature(proc_macro_internals)]
1717
#![feature(proc_macro_quote)]
1818
#![feature(rustdoc_internals)]

compiler/rustc_data_structures/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![allow(internal_features)]
1111
#![allow(rustc::default_hash_types)]
1212
#![allow(rustc::potential_query_instability)]
13+
#![cfg_attr(bootstrap, feature(lint_reasons))]
1314
#![cfg_attr(not(parallel_compiler), feature(cell_leak))]
1415
#![deny(unsafe_op_in_unsafe_fn)]
1516
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
@@ -24,7 +25,6 @@
2425
#![feature(extend_one)]
2526
#![feature(hash_raw_entry)]
2627
#![feature(hasher_prefixfree_extras)]
27-
#![feature(lint_reasons)]
2828
#![feature(macro_metavar_expr)]
2929
#![feature(map_try_insert)]
3030
#![feature(min_specialization)]

compiler/rustc_errors/src/json.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,12 @@ impl Emitter for JsonEmitter {
135135
let data: Vec<FutureBreakageItem<'_>> = diags
136136
.into_iter()
137137
.map(|mut diag| {
138-
if diag.level == crate::Level::Allow {
138+
// Allowed or expected lints don't normally (by definition) emit a lint
139+
// but future incompat lints are special and are emitted anyway.
140+
//
141+
// So to avoid ICEs and confused users we "upgrade" the lint level for
142+
// those `FutureBreakageItem` to warn.
143+
if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) {
139144
diag.level = crate::Level::Warning;
140145
}
141146
FutureBreakageItem {

compiler/rustc_feature/src/accepted.rs

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ declare_features! (
232232
(accepted, label_break_value, "1.65.0", Some(48594)),
233233
/// Allows `let...else` statements.
234234
(accepted, let_else, "1.65.0", Some(87335)),
235+
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
236+
(accepted, lint_reasons, "CURRENT_RUSTC_VERSION", Some(54503)),
235237
/// Allows `break {expr}` with a value inside `loop`s.
236238
(accepted, loop_break_value, "1.19.0", Some(37339)),
237239
/// Allows use of `?` as the Kleene "at most one" operator in macros.

compiler/rustc_feature/src/builtin_attrs.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
369369
allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
370370
DuplicatesOk, EncodeCrossCrate::No,
371371
),
372-
gated!(
373-
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
374-
EncodeCrossCrate::No, lint_reasons, experimental!(expect)
372+
ungated!(
373+
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
374+
DuplicatesOk, EncodeCrossCrate::No,
375375
),
376376
ungated!(
377377
forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),

compiler/rustc_feature/src/unstable.rs

-2
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,6 @@ declare_features! (
512512
/// Allows using `#[link(kind = "link-arg", name = "...")]`
513513
/// to pass custom arguments to the linker.
514514
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
515-
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
516-
(unstable, lint_reasons, "1.31.0", Some(54503)),
517515
/// Give access to additional metadata about declarative macro meta-variables.
518516
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
519517
/// Provides a way to concatenate identifiers using metavariable expressions.

compiler/rustc_lint/src/expect.rs

-5
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,13 @@ use rustc_middle::query::Providers;
33
use rustc_middle::ty::TyCtxt;
44
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
55
use rustc_session::lint::LintExpectationId;
6-
use rustc_span::symbol::sym;
76
use rustc_span::Symbol;
87

98
pub(crate) fn provide(providers: &mut Providers) {
109
*providers = Providers { check_expectations, ..*providers };
1110
}
1211

1312
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
14-
if !tcx.features().active(sym::lint_reasons) {
15-
return;
16-
}
17-
1813
let lint_expectations = tcx.lint_expectations(());
1914
let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids();
2015

compiler/rustc_lint/src/levels.rs

-10
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use rustc_session::lint::{
3737
},
3838
Level, Lint, LintExpectationId, LintId,
3939
};
40-
use rustc_session::parse::feature_err;
4140
use rustc_session::Session;
4241
use rustc_span::symbol::{sym, Symbol};
4342
use rustc_span::{Span, DUMMY_SP};
@@ -788,15 +787,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
788787
ast::MetaItemKind::NameValue(ref name_value) => {
789788
if item.path == sym::reason {
790789
if let ast::LitKind::Str(rationale, _) = name_value.kind {
791-
if !self.features.lint_reasons {
792-
feature_err(
793-
&self.sess,
794-
sym::lint_reasons,
795-
item.span,
796-
"lint reasons are experimental",
797-
)
798-
.emit();
799-
}
800790
reason = Some(rationale);
801791
} else {
802792
sess.dcx().emit_err(MalformedAttribute {

compiler/rustc_lint_defs/src/builtin.rs

+8-18
Original file line numberDiff line numberDiff line change
@@ -608,13 +608,13 @@ declare_lint! {
608608
}
609609

610610
declare_lint! {
611-
/// The `unfulfilled_lint_expectations` lint detects lint trigger expectations
612-
/// that have not been fulfilled.
611+
/// The `unfulfilled_lint_expectations` lint detects when a lint expectation is
612+
/// unfulfilled.
613613
///
614614
/// ### Example
615615
///
616616
/// ```rust
617-
/// #![feature(lint_reasons)]
617+
/// #![cfg_attr(bootstrap, feature(lint_reasons))]
618618
///
619619
/// #[expect(unused_variables)]
620620
/// let x = 10;
@@ -625,24 +625,14 @@ declare_lint! {
625625
///
626626
/// ### Explanation
627627
///
628-
/// It was expected that the marked code would emit a lint. This expectation
629-
/// has not been fulfilled.
628+
/// The `#[expect]` attribute can be used to create a lint expectation. The
629+
/// expectation is fulfilled, if a `#[warn]` attribute at the same location
630+
/// would result in a lint emission. If the expectation is unfulfilled,
631+
/// because no lint was emitted, this lint will be emitted on the attribute.
630632
///
631-
/// The `expect` attribute can be removed if this is intended behavior otherwise
632-
/// it should be investigated why the expected lint is no longer issued.
633-
///
634-
/// In rare cases, the expectation might be emitted at a different location than
635-
/// shown in the shown code snippet. In most cases, the `#[expect]` attribute
636-
/// works when added to the outer scope. A few lints can only be expected
637-
/// on a crate level.
638-
///
639-
/// Part of RFC 2383. The progress is being tracked in [#54503]
640-
///
641-
/// [#54503]: https://github.com/rust-lang/rust/issues/54503
642633
pub UNFULFILLED_LINT_EXPECTATIONS,
643634
Warn,
644-
"unfulfilled lint expectation",
645-
@feature_gate = rustc_span::sym::lint_reasons;
635+
"unfulfilled lint expectation"
646636
}
647637

648638
declare_lint! {

src/doc/rustc/src/lints/levels.md

+55-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# Lint Levels
22

3-
In `rustc`, lints are divided into five *levels*:
3+
In `rustc`, lints are divided into six *levels*:
44

55
1. allow
6-
2. warn
7-
3. force-warn
8-
4. deny
9-
5. forbid
6+
2. expect
7+
3. warn
8+
4. force-warn
9+
5. deny
10+
6. forbid
1011

1112
Each lint has a default level (explained in the lint listing later in this
1213
chapter), and the compiler has a default warning level. First, let's explain
@@ -33,6 +34,40 @@ But this code violates the `missing_docs` lint.
3334
These lints exist mostly to be manually turned on via configuration, as we'll
3435
talk about later in this section.
3536

37+
## expect
38+
39+
Sometimes, it can be helpful to suppress lints, but at the same time ensure that
40+
the code in question still emits them. The 'expect' level does exactly this. If
41+
the lint in question is not emitted, the `unfulfilled_lint_expectation` lint
42+
triggers on the `expect` attribute, notifying you that the expectation is no
43+
longer fulfilled.
44+
45+
```rust
46+
fn main() {
47+
#[expect(unused_variables)]
48+
let unused = "Everyone ignores me";
49+
50+
#[expect(unused_variables)] // `unused_variables` lint is not emitted
51+
let used = "I'm useful"; // the expectation is therefore unfulfilled
52+
println!("The `used` value is equal to: {:?}", used);
53+
}
54+
```
55+
56+
This will produce the following warning:
57+
58+
```txt
59+
warning: this lint expectation is unfulfilled
60+
--> src/main.rs:7:14
61+
|
62+
7 | #[expect(unused_variables)]
63+
| ^^^^^^^^^^^^^^^^
64+
|
65+
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
66+
```
67+
68+
This level can only be defined via the `#[expect]` attribute, there is no equivalent
69+
flag. Lints with the special 'force-warn' level will still be emitted as usual.
70+
3671
## warn
3772

3873
The 'warn' lint level will produce a warning if you violate the lint. For example,
@@ -240,6 +275,21 @@ And use multiple attributes together:
240275
pub fn foo() {}
241276
```
242277
278+
All lint attributes support an additional `reason` parameter, to give context why
279+
a certain attribute was added. This reason will be displayed as part of the lint
280+
message, if the lint is emitted at the defined level.
281+
282+
```rust
283+
use std::path::PathBuf;
284+
pub fn get_path() -> PathBuf {
285+
#[allow(unused_mut, reason = "this is only modified on some platforms")]
286+
let mut file_name = PathBuf::from("git");
287+
#[cfg(target_os = "windows")]
288+
file_name.set_extension("exe");
289+
file_name
290+
}
291+
```
292+
243293
### Capping lints
244294
245295
`rustc` supports a flag, `--cap-lints LEVEL` that sets the "lint cap level."

src/tools/clippy/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,7 @@ Released 2022-05-19
19431943
[#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
19441944
* [`needless_match`]
19451945
[#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
1946-
* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
1946+
* [`allow_attributes_without_reason`]
19471947
[#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
19481948
* [`print_in_format_impl`]
19491949
[#8253](https://github.com/rust-lang/rust-clippy/pull/8253)

src/tools/clippy/book/src/lint_configuration.md

+2
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,8 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
669669

670670
---
671671
**Affected lints:**
672+
* [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes)
673+
* [`allow_attributes_without_reason`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason)
672674
* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
673675
* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
674676
* [`assigning_clones`](https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones)

src/tools/clippy/clippy_config/src/conf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ define_Conf! {
265265
///
266266
/// Suppress lints whenever the suggested change would cause breakage for other crates.
267267
(avoid_breaking_exported_api: bool = true),
268-
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS.
268+
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON.
269269
///
270270
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
271271
#[default_text = ""]

src/tools/clippy/clippy_config/src/msrvs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ macro_rules! msrv_aliases {
1717

1818
// names may refer to stabilized feature flags or library items
1919
msrv_aliases! {
20+
1,81,0 { LINT_REASONS_STABILIZATION }
2021
1,77,0 { C_STR_LITERALS }
2122
1,76,0 { PTR_FROM_REF }
2223
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }

src/tools/clippy/clippy_lints/src/allow_attributes.rs

-74
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_from_proc_macro;
3+
use rustc_ast::{AttrStyle, Attribute};
4+
use rustc_errors::Applicability;
5+
use rustc_lint::{LateContext, LintContext};
6+
use rustc_middle::lint::in_external_macro;
7+
use super::ALLOW_ATTRIBUTES;
8+
9+
// Separate each crate's features.
10+
pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) {
11+
if !in_external_macro(cx.sess(), attr.span)
12+
&& let AttrStyle::Outer = attr.style
13+
&& let Some(ident) = attr.ident()
14+
&& !is_from_proc_macro(cx, &attr)
15+
{
16+
span_lint_and_sugg(
17+
cx,
18+
ALLOW_ATTRIBUTES,
19+
ident.span,
20+
"#[allow] attribute found",
21+
"replace it with",
22+
"expect".into(),
23+
Applicability::MachineApplicable,
24+
);
25+
}
26+
}

src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs

-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ use rustc_span::sym;
88
use rustc_span::symbol::Symbol;
99

1010
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
11-
// Check for the feature
12-
if !cx.tcx.features().lint_reasons {
13-
return;
14-
}
15-
1611
// Check if the reason is present
1712
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
1813
&& let MetaItemKind::NameValue(_) = &item.kind

0 commit comments

Comments
 (0)