Skip to content

Commit a571179

Browse files
committedMay 9, 2022
Auto merge of #95542 - xFrednet:rfc-2383-expect-query, r=wesleywiser
Support tool lints with the `#[expect]` attribute (RFC 2383) This PR fixes the ICE #94953 by making the assert for converted expectation IDs conditional. Additionally, it moves the lint expectation check into a separate query to support rustdoc and other tools. On the way, I've also added some tests to ensure that the attribute works for Clippy and rustdoc lints. The number of changes comes from the long test file. This may look like a monster PR, this may smell like a monster PR and this may be a monster PR, but it's a harmless monster. 🦕 --- Closes: #94953 cc: #85549 r? `@wesleywiser` cc: `@rust-lang/rustdoc`
2 parents cb12198 + 9516a40 commit a571179

File tree

18 files changed

+663
-19
lines changed

18 files changed

+663
-19
lines changed
 

‎compiler/rustc_errors/src/lib.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,13 @@ struct HandlerInner {
426426

427427
future_breakage_diagnostics: Vec<Diagnostic>,
428428

429+
/// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is
430+
/// dropped. However, it can have values if the compilation is stopped early
431+
/// or is only partially executed. To avoid ICEs, like in rust#94953 we only
432+
/// check if [`Self::unstable_expect_diagnostics`] is empty, if the expectation ids
433+
/// have been converted.
434+
check_unstable_expect_diagnostics: bool,
435+
429436
/// Expected [`Diagnostic`]s store a [`LintExpectationId`] as part of
430437
/// the lint level. [`LintExpectationId`]s created early during the compilation
431438
/// (before `HirId`s have been defined) are not stable and can therefore not be
@@ -497,10 +504,12 @@ impl Drop for HandlerInner {
497504
);
498505
}
499506

500-
assert!(
501-
self.unstable_expect_diagnostics.is_empty(),
502-
"all diagnostics with unstable expectations should have been converted",
503-
);
507+
if self.check_unstable_expect_diagnostics {
508+
assert!(
509+
self.unstable_expect_diagnostics.is_empty(),
510+
"all diagnostics with unstable expectations should have been converted",
511+
);
512+
}
504513
}
505514
}
506515

@@ -574,6 +583,7 @@ impl Handler {
574583
emitted_diagnostics: Default::default(),
575584
stashed_diagnostics: Default::default(),
576585
future_breakage_diagnostics: Vec::new(),
586+
check_unstable_expect_diagnostics: false,
577587
unstable_expect_diagnostics: Vec::new(),
578588
fulfilled_expectations: Default::default(),
579589
}),
@@ -988,12 +998,13 @@ impl Handler {
988998
&self,
989999
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
9901000
) {
991-
let diags = std::mem::take(&mut self.inner.borrow_mut().unstable_expect_diagnostics);
1001+
let mut inner = self.inner.borrow_mut();
1002+
let diags = std::mem::take(&mut inner.unstable_expect_diagnostics);
1003+
inner.check_unstable_expect_diagnostics = true;
9921004
if diags.is_empty() {
9931005
return;
9941006
}
9951007

996-
let mut inner = self.inner.borrow_mut();
9971008
for mut diag in diags.into_iter() {
9981009
diag.update_unstable_expectation_id(unstable_to_stable);
9991010

‎compiler/rustc_interface/src/passes.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,10 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
10091009
});
10101010
}
10111011
);
1012+
1013+
// This check has to be run after all lints are done processing. We don't
1014+
// define a lint filter, as all lint checks should have finished at this point.
1015+
sess.time("check_lint_expectations", || tcx.check_expectations(None));
10121016
});
10131017

10141018
Ok(())

‎compiler/rustc_lint/src/expect.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
use crate::builtin;
22
use rustc_hir::HirId;
3+
use rustc_middle::ty::query::Providers;
34
use rustc_middle::{lint::LintExpectation, ty::TyCtxt};
45
use rustc_session::lint::LintExpectationId;
56
use rustc_span::symbol::sym;
7+
use rustc_span::Symbol;
68

7-
pub fn check_expectations(tcx: TyCtxt<'_>) {
9+
pub(crate) fn provide(providers: &mut Providers) {
10+
*providers = Providers { check_expectations, ..*providers };
11+
}
12+
13+
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
814
if !tcx.sess.features_untracked().enabled(sym::lint_reasons) {
915
return;
1016
}
@@ -13,7 +19,9 @@ pub fn check_expectations(tcx: TyCtxt<'_>) {
1319
let lint_expectations = &tcx.lint_levels(()).lint_expectations;
1420

1521
for (id, expectation) in lint_expectations {
16-
if !fulfilled_expectations.contains(id) {
22+
if !fulfilled_expectations.contains(id)
23+
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
24+
{
1725
// This check will always be true, since `lint_expectations` only
1826
// holds stable ids
1927
if let LintExpectationId::Stable { hir_id, .. } = id {

‎compiler/rustc_lint/src/late.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,4 @@ pub fn check_crate<'tcx, T: LateLintPass<'tcx>>(
503503
});
504504
},
505505
);
506-
507-
// This check has to be run after all lints are done processing for this crate
508-
tcx.sess.time("check_lint_expectations", || crate::expect::check_expectations(tcx));
509506
}

‎compiler/rustc_lint/src/levels.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,12 @@ impl<'s> LintLevelsBuilder<'s> {
371371
};
372372
self.lint_expectations.push((
373373
expect_id,
374-
LintExpectation::new(reason, sp, is_unfulfilled_lint_expectations),
374+
LintExpectation::new(
375+
reason,
376+
sp,
377+
is_unfulfilled_lint_expectations,
378+
tool_name,
379+
),
375380
));
376381
}
377382
let src = LintLevelSource::Node(
@@ -400,8 +405,10 @@ impl<'s> LintLevelsBuilder<'s> {
400405
self.insert_spec(*id, (level, src));
401406
}
402407
if let Level::Expect(expect_id) = level {
403-
self.lint_expectations
404-
.push((expect_id, LintExpectation::new(reason, sp, false)));
408+
self.lint_expectations.push((
409+
expect_id,
410+
LintExpectation::new(reason, sp, false, tool_name),
411+
));
405412
}
406413
}
407414
Err((Some(ids), ref new_lint_name)) => {
@@ -444,8 +451,10 @@ impl<'s> LintLevelsBuilder<'s> {
444451
self.insert_spec(*id, (level, src));
445452
}
446453
if let Level::Expect(expect_id) = level {
447-
self.lint_expectations
448-
.push((expect_id, LintExpectation::new(reason, sp, false)));
454+
self.lint_expectations.push((
455+
expect_id,
456+
LintExpectation::new(reason, sp, false, tool_name),
457+
));
449458
}
450459
}
451460
Err((None, _)) => {
@@ -550,8 +559,10 @@ impl<'s> LintLevelsBuilder<'s> {
550559
}
551560
}
552561
if let Level::Expect(expect_id) = level {
553-
self.lint_expectations
554-
.push((expect_id, LintExpectation::new(reason, sp, false)));
562+
self.lint_expectations.push((
563+
expect_id,
564+
LintExpectation::new(reason, sp, false, tool_name),
565+
));
555566
}
556567
} else {
557568
panic!("renamed lint does not exist: {}", new_name);

‎compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub use rustc_session::lint::{LintArray, LintPass};
109109

110110
pub fn provide(providers: &mut Providers) {
111111
levels::provide(providers);
112+
expect::provide(providers);
112113
*providers = Providers { lint_mod, ..*providers };
113114
}
114115

‎compiler/rustc_middle/src/lint.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,20 @@ pub struct LintExpectation {
210210
/// adjusted to include an additional note. Therefore, we have to track if
211211
/// the expectation is for the lint.
212212
pub is_unfulfilled_lint_expectations: bool,
213+
/// This will hold the name of the tool that this lint belongs to. For
214+
/// the lint `clippy::some_lint` the tool would be `clippy`, the same
215+
/// goes for `rustdoc`. This will be `None` for rustc lints
216+
pub lint_tool: Option<Symbol>,
213217
}
214218

215219
impl LintExpectation {
216220
pub fn new(
217221
reason: Option<Symbol>,
218222
emission_span: Span,
219223
is_unfulfilled_lint_expectations: bool,
224+
lint_tool: Option<Symbol>,
220225
) -> Self {
221-
Self { reason, emission_span, is_unfulfilled_lint_expectations }
226+
Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
222227
}
223228
}
224229

‎compiler/rustc_middle/src/query/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,25 @@ rustc_queries! {
157157
desc { "running analysis passes on this crate" }
158158
}
159159

160+
/// This query checks the fulfillment of collected lint expectations.
161+
/// All lint emitting queries have to be done before this is executed
162+
/// to ensure that all expectations can be fulfilled.
163+
///
164+
/// This is an extra query to enable other drivers (like rustdoc) to
165+
/// only execute a small subset of the `analysis` query, while allowing
166+
/// lints to be expected. In rustc, this query will be executed as part of
167+
/// the `analysis` query and doesn't have to be called a second time.
168+
///
169+
/// Tools can additionally pass in a tool filter. That will restrict the
170+
/// expectations to only trigger for lints starting with the listed tool
171+
/// name. This is useful for cases were not all linting code from rustc
172+
/// was called. With the default `None` all registered lints will also
173+
/// be checked for expectation fulfillment.
174+
query check_expectations(key: Option<Symbol>) -> () {
175+
eval_always
176+
desc { "checking lint expectations (RFC 2383)" }
177+
}
178+
160179
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
161180
/// associated generics.
162181
query generics_of(key: DefId) -> ty::Generics {

‎compiler/rustc_query_impl/src/keys.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,16 @@ impl Key for Symbol {
435435
}
436436
}
437437

438+
impl Key for Option<Symbol> {
439+
#[inline(always)]
440+
fn query_crate_is_local(&self) -> bool {
441+
true
442+
}
443+
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
444+
DUMMY_SP
445+
}
446+
}
447+
438448
/// Canonical query goals correspond to abstract trait operations that
439449
/// are not tied to any crate in particular.
440450
impl<'tcx, T> Key for Canonical<'tcx, T> {

‎src/librustdoc/core.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ crate fn create_config(
232232
rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_string(),
233233
rustc_lint::builtin::UNKNOWN_LINTS.name.to_string(),
234234
rustc_lint::builtin::UNEXPECTED_CFGS.name.to_string(),
235+
// this lint is needed to support `#[expect]` attributes
236+
rustc_lint::builtin::UNFULFILLED_LINT_EXPECTATIONS.name.to_string(),
235237
];
236238
lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string()));
237239

@@ -463,6 +465,8 @@ crate fn run_global_ctxt(
463465
}
464466
}
465467

468+
tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
469+
466470
if tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() {
467471
rustc_errors::FatalError.raise();
468472
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// check-pass
2+
#![feature(lint_reasons)]
3+
4+
//! This file tests the `#[expect]` attribute implementation for tool lints. The same
5+
//! file is used to test clippy and rustdoc. Any changes to this file should be synced
6+
//! to the other test files as well.
7+
//!
8+
//! Expectations:
9+
//! * rustc: only rustc lint expectations are emitted
10+
//! * clippy: rustc and Clippy's expectations are emitted
11+
//! * rustdoc: only rustdoc lint expectations are emitted
12+
//!
13+
//! This test can't cover every lint from Clippy, rustdoc and potentially other
14+
//! tools that will be developed. This therefore only tests a small subset of lints
15+
16+
#![expect(rustdoc::missing_crate_level_docs)]
17+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
18+
//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default
19+
20+
mod rustc_ok {
21+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
22+
23+
#[expect(dead_code)]
24+
pub fn rustc_lints() {
25+
let x = 42.0;
26+
27+
#[expect(illegal_floating_point_literal_pattern)]
28+
match x {
29+
5.0 => {}
30+
6.0 => {}
31+
_ => {}
32+
}
33+
}
34+
}
35+
36+
mod rustc_warn {
37+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
38+
39+
#[expect(dead_code)]
40+
pub fn rustc_lints() {
41+
let x = 42;
42+
43+
#[expect(illegal_floating_point_literal_pattern)]
44+
match x {
45+
5 => {}
46+
6 => {}
47+
_ => {}
48+
}
49+
}
50+
}
51+
52+
pub mod rustdoc_ok {
53+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
54+
55+
#[expect(rustdoc::broken_intra_doc_links)]
56+
/// I want to link to [`Nonexistent`] but it doesn't exist!
57+
pub fn foo() {}
58+
59+
#[expect(rustdoc::invalid_html_tags)]
60+
/// <h1>
61+
pub fn bar() {}
62+
63+
#[expect(rustdoc::bare_urls)]
64+
/// http://example.org
65+
pub fn baz() {}
66+
}
67+
68+
pub mod rustdoc_warn {
69+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
70+
71+
#[expect(rustdoc::broken_intra_doc_links)]
72+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
73+
/// I want to link to [`bar`] but it doesn't exist!
74+
pub fn foo() {}
75+
76+
#[expect(rustdoc::invalid_html_tags)]
77+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
78+
/// <h1></h1>
79+
pub fn bar() {}
80+
81+
#[expect(rustdoc::bare_urls)]
82+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
83+
/// <http://example.org>
84+
pub fn baz() {}
85+
}
86+
87+
mod clippy_ok {
88+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
89+
90+
#[expect(clippy::almost_swapped)]
91+
fn foo() {
92+
let mut a = 0;
93+
let mut b = 9;
94+
a = b;
95+
b = a;
96+
}
97+
98+
#[expect(clippy::bytes_nth)]
99+
fn bar() {
100+
let _ = "Hello".bytes().nth(3);
101+
}
102+
103+
#[expect(clippy::if_same_then_else)]
104+
fn baz() {
105+
let _ = if true {
106+
42
107+
} else {
108+
42
109+
};
110+
}
111+
112+
#[expect(clippy::logic_bug)]
113+
fn burger() {
114+
let a = false;
115+
let b = true;
116+
117+
if a && b || a {}
118+
}
119+
}
120+
121+
mod clippy_warn {
122+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
123+
124+
#[expect(clippy::almost_swapped)]
125+
fn foo() {
126+
let mut a = 0;
127+
let mut b = 9;
128+
a = b;
129+
}
130+
131+
#[expect(clippy::bytes_nth)]
132+
fn bar() {
133+
let _ = "Hello".as_bytes().get(3);
134+
}
135+
136+
#[expect(clippy::if_same_then_else)]
137+
fn baz() {
138+
let _ = if true {
139+
33
140+
} else {
141+
42
142+
};
143+
}
144+
145+
#[expect(clippy::logic_bug)]
146+
fn burger() {
147+
let a = false;
148+
let b = true;
149+
let c = false;
150+
151+
if a && b || c {}
152+
}
153+
}
154+
155+
fn main() {
156+
rustc_warn::rustc_lints();
157+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
warning: this lint expectation is unfulfilled
2+
--> $DIR/expect-tool-lint-rfc-2383.rs:16:11
3+
|
4+
LL | #![expect(rustdoc::missing_crate_level_docs)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
8+
9+
warning: this lint expectation is unfulfilled
10+
--> $DIR/expect-tool-lint-rfc-2383.rs:71:14
11+
|
12+
LL | #[expect(rustdoc::broken_intra_doc_links)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
warning: this lint expectation is unfulfilled
16+
--> $DIR/expect-tool-lint-rfc-2383.rs:76:14
17+
|
18+
LL | #[expect(rustdoc::invalid_html_tags)]
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
warning: this lint expectation is unfulfilled
22+
--> $DIR/expect-tool-lint-rfc-2383.rs:81:14
23+
|
24+
LL | #[expect(rustdoc::bare_urls)]
25+
| ^^^^^^^^^^^^^^^^^^
26+
27+
warning: 4 warnings emitted
28+
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// check-pass
2+
#![feature(lint_reasons)]
3+
4+
//! This file tests the `#[expect]` attribute implementation for tool lints. The same
5+
//! file is used to test clippy and rustdoc. Any changes to this file should be synced
6+
//! to the other test files.
7+
//!
8+
//! Expectations:
9+
//! * rustc: only rustc lint expectations are emitted
10+
//! * clippy: rustc and Clippy's expectations are emitted
11+
//! * rustdoc: only rustdoc lint expectations are emitted
12+
//!
13+
//! This test can't cover every lint from Clippy, rustdoc and potentially other
14+
//! tools that will be developed. This therefore only tests a small subset of lints
15+
16+
#![expect(rustdoc::missing_crate_level_docs)]
17+
18+
mod rustc_ok {
19+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
20+
21+
#[expect(dead_code)]
22+
pub fn rustc_lints() {
23+
let x = 42.0;
24+
25+
#[expect(illegal_floating_point_literal_pattern)]
26+
match x {
27+
5.0 => {}
28+
6.0 => {}
29+
_ => {}
30+
}
31+
}
32+
}
33+
34+
mod rustc_warn {
35+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
36+
37+
#[expect(dead_code)]
38+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
39+
//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default
40+
pub fn rustc_lints() {
41+
let x = 42;
42+
43+
#[expect(illegal_floating_point_literal_pattern)]
44+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
45+
match x {
46+
5 => {}
47+
6 => {}
48+
_ => {}
49+
}
50+
}
51+
}
52+
53+
pub mod rustdoc_ok {
54+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
55+
56+
#[expect(rustdoc::broken_intra_doc_links)]
57+
/// I want to link to [`Nonexistent`] but it doesn't exist!
58+
pub fn foo() {}
59+
60+
#[expect(rustdoc::invalid_html_tags)]
61+
/// <h1>
62+
pub fn bar() {}
63+
64+
#[expect(rustdoc::bare_urls)]
65+
/// http://example.org
66+
pub fn baz() {}
67+
}
68+
69+
pub mod rustdoc_warn {
70+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
71+
72+
#[expect(rustdoc::broken_intra_doc_links)]
73+
/// I want to link to [`bar`] but it doesn't exist!
74+
pub fn foo() {}
75+
76+
#[expect(rustdoc::invalid_html_tags)]
77+
/// <h1></h1>
78+
pub fn bar() {}
79+
80+
#[expect(rustdoc::bare_urls)]
81+
/// <http://example.org>
82+
pub fn baz() {}
83+
}
84+
85+
mod clippy_ok {
86+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
87+
88+
#[expect(clippy::almost_swapped)]
89+
fn foo() {
90+
let mut a = 0;
91+
let mut b = 9;
92+
a = b;
93+
b = a;
94+
}
95+
96+
#[expect(clippy::bytes_nth)]
97+
fn bar() {
98+
let _ = "Hello".bytes().nth(3);
99+
}
100+
101+
#[expect(clippy::if_same_then_else)]
102+
fn baz() {
103+
let _ = if true {
104+
42
105+
} else {
106+
42
107+
};
108+
}
109+
110+
#[expect(clippy::logic_bug)]
111+
fn burger() {
112+
let a = false;
113+
let b = true;
114+
115+
if a && b || a {}
116+
}
117+
}
118+
119+
mod clippy_warn {
120+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
121+
122+
#[expect(clippy::almost_swapped)]
123+
fn foo() {
124+
let mut a = 0;
125+
let mut b = 9;
126+
a = b;
127+
}
128+
129+
#[expect(clippy::bytes_nth)]
130+
fn bar() {
131+
let _ = "Hello".as_bytes().get(3);
132+
}
133+
134+
#[expect(clippy::if_same_then_else)]
135+
fn baz() {
136+
let _ = if true {
137+
33
138+
} else {
139+
42
140+
};
141+
}
142+
143+
#[expect(clippy::logic_bug)]
144+
fn burger() {
145+
let a = false;
146+
let b = true;
147+
let c = false;
148+
149+
if a && b || c {}
150+
}
151+
}
152+
153+
fn main() {
154+
rustc_warn::rustc_lints();
155+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
warning: this lint expectation is unfulfilled
2+
--> $DIR/expect_tool_lint_rfc_2383.rs:37:14
3+
|
4+
LL | #[expect(dead_code)]
5+
| ^^^^^^^^^
6+
|
7+
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
8+
9+
warning: this lint expectation is unfulfilled
10+
--> $DIR/expect_tool_lint_rfc_2383.rs:43:18
11+
|
12+
LL | #[expect(illegal_floating_point_literal_pattern)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
warning: 2 warnings emitted
16+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This ensures that ICEs like rust#94953 don't happen
2+
// check-pass
3+
// compile-flags: -Z unpretty=expanded
4+
5+
#![feature(lint_reasons)]
6+
7+
// This `expect` will create an expectation with an unstable expectation id
8+
#[expect(while_true)]
9+
fn create_early_lint_pass_expectation() {
10+
// `while_true` is an early lint
11+
while true {}
12+
}
13+
14+
fn main() {
15+
create_early_lint_pass_expectation();
16+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(prelude_import)]
2+
#![no_std]
3+
// This ensures that ICEs like rust#94953 don't happen
4+
// check-pass
5+
// compile-flags: -Z unpretty=expanded
6+
7+
#![feature(lint_reasons)]
8+
#[prelude_import]
9+
use ::std::prelude::rust_2015::*;
10+
#[macro_use]
11+
extern crate std;
12+
13+
// This `expect` will create an expectation with an unstable expectation id
14+
#[expect(while_true)]
15+
fn create_early_lint_pass_expectation() {
16+
// `while_true` is an early lint
17+
while true {}
18+
}
19+
20+
fn main() { create_early_lint_pass_expectation(); }
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// check-pass
2+
#![feature(lint_reasons)]
3+
//! This file tests the `#[expect]` attribute implementation for tool lints. The same
4+
//! file is used to test clippy and rustdoc. Any changes to this file should be synced
5+
//! to the other test files as well.
6+
//!
7+
//! Expectations:
8+
//! * rustc: only rustc lint expectations are emitted
9+
//! * clippy: rustc and Clippy's expectations are emitted
10+
//! * rustdoc: only rustdoc lint expectations are emitted
11+
//!
12+
//! This test can't cover every lint from Clippy, rustdoc and potentially other
13+
//! tools that will be developed. This therefore only tests a small subset of lints
14+
#![expect(rustdoc::missing_crate_level_docs)]
15+
16+
mod rustc_ok {
17+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
18+
19+
#[expect(dead_code)]
20+
pub fn rustc_lints() {
21+
let x = 42.0;
22+
23+
#[expect(illegal_floating_point_literal_pattern)]
24+
match x {
25+
5.0 => {}
26+
6.0 => {}
27+
_ => {}
28+
}
29+
}
30+
}
31+
32+
mod rustc_warn {
33+
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
34+
35+
#[expect(dead_code)]
36+
pub fn rustc_lints() {
37+
let x = 42;
38+
39+
#[expect(illegal_floating_point_literal_pattern)]
40+
match x {
41+
5 => {}
42+
6 => {}
43+
_ => {}
44+
}
45+
}
46+
}
47+
48+
pub mod rustdoc_ok {
49+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
50+
51+
#[expect(rustdoc::broken_intra_doc_links)]
52+
/// I want to link to [`Nonexistent`] but it doesn't exist!
53+
pub fn foo() {}
54+
55+
#[expect(rustdoc::invalid_html_tags)]
56+
/// <h1>
57+
pub fn bar() {}
58+
59+
#[expect(rustdoc::bare_urls)]
60+
/// http://example.org
61+
pub fn baz() {}
62+
}
63+
64+
pub mod rustdoc_warn {
65+
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
66+
67+
#[expect(rustdoc::broken_intra_doc_links)]
68+
/// I want to link to [`bar`] but it doesn't exist!
69+
pub fn foo() {}
70+
71+
#[expect(rustdoc::invalid_html_tags)]
72+
/// <h1></h1>
73+
pub fn bar() {}
74+
75+
#[expect(rustdoc::bare_urls)]
76+
/// <http://example.org>
77+
pub fn baz() {}
78+
}
79+
80+
mod clippy_ok {
81+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
82+
83+
#[expect(clippy::almost_swapped)]
84+
fn foo() {
85+
let mut a = 0;
86+
let mut b = 9;
87+
a = b;
88+
b = a;
89+
}
90+
91+
#[expect(clippy::bytes_nth)]
92+
fn bar() {
93+
let _ = "Hello".bytes().nth(3);
94+
}
95+
96+
#[expect(clippy::if_same_then_else)]
97+
fn baz() {
98+
let _ = if true { 42 } else { 42 };
99+
}
100+
101+
#[expect(clippy::logic_bug)]
102+
fn burger() {
103+
let a = false;
104+
let b = true;
105+
106+
if a && b || a {}
107+
}
108+
}
109+
110+
mod clippy_warn {
111+
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
112+
113+
#[expect(clippy::almost_swapped)]
114+
fn foo() {
115+
let mut a = 0;
116+
let mut b = 9;
117+
a = b;
118+
}
119+
120+
#[expect(clippy::bytes_nth)]
121+
fn bar() {
122+
let _ = "Hello".as_bytes().get(3);
123+
}
124+
125+
#[expect(clippy::if_same_then_else)]
126+
fn baz() {
127+
let _ = if true { 33 } else { 42 };
128+
}
129+
130+
#[expect(clippy::logic_bug)]
131+
fn burger() {
132+
let a = false;
133+
let b = true;
134+
let c = false;
135+
136+
if a && b || c {}
137+
}
138+
}
139+
140+
fn main() {
141+
rustc_warn::rustc_lints();
142+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: this lint expectation is unfulfilled
2+
--> $DIR/expect_tool_lint_rfc_2383.rs:35:14
3+
|
4+
LL | #[expect(dead_code)]
5+
| ^^^^^^^^^
6+
|
7+
= note: `-D unfulfilled-lint-expectations` implied by `-D warnings`
8+
9+
error: this lint expectation is unfulfilled
10+
--> $DIR/expect_tool_lint_rfc_2383.rs:39:18
11+
|
12+
LL | #[expect(illegal_floating_point_literal_pattern)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: this lint expectation is unfulfilled
16+
--> $DIR/expect_tool_lint_rfc_2383.rs:113:14
17+
|
18+
LL | #[expect(clippy::almost_swapped)]
19+
| ^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: this lint expectation is unfulfilled
22+
--> $DIR/expect_tool_lint_rfc_2383.rs:120:14
23+
|
24+
LL | #[expect(clippy::bytes_nth)]
25+
| ^^^^^^^^^^^^^^^^^
26+
27+
error: this lint expectation is unfulfilled
28+
--> $DIR/expect_tool_lint_rfc_2383.rs:125:14
29+
|
30+
LL | #[expect(clippy::if_same_then_else)]
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: this lint expectation is unfulfilled
34+
--> $DIR/expect_tool_lint_rfc_2383.rs:130:14
35+
|
36+
LL | #[expect(clippy::logic_bug)]
37+
| ^^^^^^^^^^^^^^^^^
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)
Please sign in to comment.