Skip to content

Commit 72c69e1

Browse files
committed
Check for MSRV attributes in late passes using the HIR
1 parent 6a3ef93 commit 72c69e1

File tree

11 files changed

+218
-195
lines changed

11 files changed

+218
-195
lines changed

book/src/development/adding_lints.md

+14-17
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ pub struct ManualStrip {
459459

460460
impl ManualStrip {
461461
pub fn new(conf: &'static Conf) -> Self {
462-
Self { msrv: conf.msrv.clone() }
462+
Self { msrv: conf.msrv }
463463
}
464464
}
465465
```
@@ -468,24 +468,13 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass
468468
using the `Msrv::meets` method.
469469

470470
``` rust
471-
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
471+
if !self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) {
472472
return;
473473
}
474474
```
475475

476-
The project's MSRV can also be specified as an attribute, which overrides
477-
the value from `clippy.toml`. This can be accounted for using the
478-
`extract_msrv_attr!(LintContext)` macro and passing
479-
`LateContext`/`EarlyContext`.
480-
481-
```rust,ignore
482-
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
483-
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
484-
...
485-
}
486-
extract_msrv_attr!(LateContext);
487-
}
488-
```
476+
Early lint passes should instead use `MsrvStack` coupled with
477+
`extract_msrv_attr!()`
489478

490479
Once the `msrv` is added to the lint, a relevant test case should be added to
491480
the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
@@ -511,8 +500,16 @@ in `clippy_config/src/conf.rs`:
511500

512501
```rust
513502
define_Conf! {
514-
/// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
515-
(msrv: Option<String> = None),
503+
#[lints(
504+
allow_attributes,
505+
allow_attributes_without_reason,
506+
..
507+
<the newly added lint name>,
508+
..
509+
unused_trait_names,
510+
use_self,
511+
)]
512+
msrv: Msrv = Msrv::default(),
516513
...
517514
}
518515
```

clippy_config/src/conf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ define_Conf! {
625625
unused_trait_names,
626626
use_self,
627627
)]
628-
msrv: Msrv = Msrv::empty(),
628+
msrv: Msrv = Msrv::default(),
629629
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
630630
#[lints(large_types_passed_by_value)]
631631
pass_by_value_size_limit: u64 = 256,

clippy_dev/src/main.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn main() {
3434
category,
3535
r#type,
3636
msrv,
37-
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
37+
} => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) {
3838
Ok(()) => update_lints::update(utils::UpdateMode::Change),
3939
Err(e) => eprintln!("Unable to create lint: {e}"),
4040
},
@@ -138,9 +138,9 @@ enum DevCommand {
138138
#[command(name = "new_lint")]
139139
/// Create a new lint and run `cargo dev update_lints`
140140
NewLint {
141-
#[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
141+
#[arg(short, long, conflicts_with = "type", default_value = "late")]
142142
/// Specify whether the lint runs during the early or late pass
143-
pass: String,
143+
pass: new_lint::Pass,
144144
#[arg(
145145
short,
146146
long,

clippy_dev/src/new_lint.rs

+46-34
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
use crate::utils::{clippy_project_root, clippy_version};
2+
use clap::ValueEnum;
23
use indoc::{formatdoc, writedoc};
3-
use std::fmt;
4-
use std::fmt::Write as _;
4+
use std::fmt::{self, Write as _};
55
use std::fs::{self, OpenOptions};
66
use std::io::prelude::*;
77
use std::io::{self, ErrorKind};
88
use std::path::{Path, PathBuf};
99

10+
#[derive(Clone, Copy, PartialEq, ValueEnum)]
11+
pub enum Pass {
12+
Early,
13+
Late,
14+
}
15+
16+
impl fmt::Display for Pass {
17+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18+
f.write_str(match self {
19+
Pass::Early => "early",
20+
Pass::Late => "late",
21+
})
22+
}
23+
}
24+
1025
struct LintData<'a> {
11-
pass: &'a str,
26+
pass: Pass,
1227
name: &'a str,
1328
category: &'a str,
1429
ty: Option<&'a str>,
@@ -36,7 +51,7 @@ impl<T> Context for io::Result<T> {
3651
/// # Errors
3752
///
3853
/// This function errors out if the files couldn't be created or written to.
39-
pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
54+
pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
4055
if category == "cargo" && ty.is_none() {
4156
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
4257
ty = Some("cargo");
@@ -57,7 +72,7 @@ pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv
5772
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
5873
}
5974

60-
if pass == "early" {
75+
if pass == Pass::Early {
6176
println!(
6277
"\n\
6378
NOTE: Use a late pass unless you need something specific from\n\
@@ -137,23 +152,17 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
137152
let mut lib_rs = fs::read_to_string(path).context("reading")?;
138153

139154
let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
155+
let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
156+
let lint_pass = lint.pass;
157+
let module_name = lint.name;
158+
let camel_name = to_camel_case(lint.name);
140159

141160
let new_lint = if enable_msrv {
142161
format!(
143162
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
144-
lint_pass = lint.pass,
145-
ctor_arg = if lint.pass == "late" { "_" } else { "" },
146-
module_name = lint.name,
147-
camel_name = to_camel_case(lint.name),
148163
)
149164
} else {
150-
format!(
151-
"store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",
152-
lint_pass = lint.pass,
153-
ctor_arg = if lint.pass == "late" { "_" } else { "" },
154-
module_name = lint.name,
155-
camel_name = to_camel_case(lint.name),
156-
)
165+
format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",)
157166
};
158167

159168
lib_rs.insert_str(comment_start, &new_lint);
@@ -243,11 +252,16 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
243252
let mut result = String::new();
244253

245254
let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
246-
"early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
247-
"late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
248-
_ => {
249-
unreachable!("`pass_type` should only ever be `early` or `late`!");
250-
},
255+
Pass::Early => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
256+
Pass::Late => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
257+
};
258+
let (msrv_ty, msrv_ctor, extract_msrv) = match lint.pass {
259+
Pass::Early => (
260+
"MsrvStack",
261+
"MsrvStack::new(conf.msrv)",
262+
"\n extract_msrv_attr!();\n",
263+
),
264+
Pass::Late => ("Msrv", "conf.msrv", ""),
251265
};
252266

253267
let lint_name = lint.name;
@@ -258,10 +272,10 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
258272
result.push_str(&if enable_msrv {
259273
formatdoc!(
260274
r"
261-
use clippy_utils::msrvs::{{self, Msrv}};
275+
use clippy_utils::msrvs::{{self, {msrv_ty}}};
262276
use clippy_config::Conf;
263277
{pass_import}
264-
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
278+
use rustc_lint::{{{context_import}, {pass_type}}};
265279
use rustc_session::impl_lint_pass;
266280
267281
"
@@ -283,20 +297,18 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
283297
formatdoc!(
284298
r"
285299
pub struct {name_camel} {{
286-
msrv: Msrv,
300+
msrv: {msrv_ty},
287301
}}
288302
289303
impl {name_camel} {{
290304
pub fn new(conf: &'static Conf) -> Self {{
291-
Self {{ msrv: conf.msrv.clone() }}
305+
Self {{ msrv: {msrv_ctor} }}
292306
}}
293307
}}
294308
295309
impl_lint_pass!({name_camel} => [{name_upper}]);
296310
297-
impl {pass_type}{pass_lifetimes} for {name_camel} {{
298-
extract_msrv_attr!({context_import});
299-
}}
311+
impl {pass_type}{pass_lifetimes} for {name_camel} {{{extract_msrv}}}
300312
301313
// TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
302314
// TODO: Update msrv config comment in `clippy_config/src/conf.rs`
@@ -372,9 +384,9 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
372384

373385
let mod_file_path = ty_dir.join("mod.rs");
374386
let context_import = setup_mod_file(&mod_file_path, lint)?;
375-
let pass_lifetimes = match context_import {
376-
"LateContext" => "<'_>",
377-
_ => "",
387+
let (pass_lifetimes, msrv_ty, msrv_ref, msrv_cx) = match context_import {
388+
"LateContext" => ("<'_>", "Msrv", "", "cx, "),
389+
_ => ("", "MsrvStack", "&", ""),
378390
};
379391

380392
let name_upper = lint.name.to_uppercase();
@@ -384,14 +396,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
384396
let _: fmt::Result = writedoc!(
385397
lint_file_contents,
386398
r#"
387-
use clippy_utils::msrvs::{{self, Msrv}};
399+
use clippy_utils::msrvs::{{self, {msrv_ty}}};
388400
use rustc_lint::{{{context_import}, LintContext}};
389401
390402
use super::{name_upper};
391403
392404
// TODO: Adjust the parameters as necessary
393-
pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: &Msrv) {{
394-
if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
405+
pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: {msrv_ref}{msrv_ty}) {{
406+
if !msrv.meets({msrv_cx}todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
395407
return;
396408
}}
397409
todo!();

clippy_utils/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ use rustc_middle::hir::nested_filter;
135135

136136
#[macro_export]
137137
macro_rules! extract_msrv_attr {
138-
($context:ident) => {
139-
fn check_attributes(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
138+
() => {
139+
fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
140140
let sess = rustc_lint::LintContext::sess(cx);
141141
self.msrv.check_attributes(sess, attrs);
142142
}
143143

144-
fn check_attributes_post(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
144+
fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
145145
let sess = rustc_lint::LintContext::sess(cx);
146146
self.msrv.check_attributes_post(sess, attrs);
147147
}

0 commit comments

Comments
 (0)