Skip to content

Commit 0b1f5c8

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

File tree

96 files changed

+528
-656
lines changed

Some content is hidden

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

96 files changed

+528
-656
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_lints/src/almost_complete_range.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
3-
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::msrvs::{self, MsrvStack};
44
use clippy_utils::source::{trim_span, walk_span_to_context};
55
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
66
use rustc_errors::Applicability;
@@ -32,12 +32,12 @@ declare_clippy_lint! {
3232
impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]);
3333

3434
pub struct AlmostCompleteRange {
35-
msrv: Msrv,
35+
msrv: MsrvStack,
3636
}
3737
impl AlmostCompleteRange {
3838
pub fn new(conf: &'static Conf) -> Self {
3939
Self {
40-
msrv: conf.msrv.clone(),
40+
msrv: MsrvStack::new(conf.msrv),
4141
}
4242
}
4343
}
@@ -97,7 +97,7 @@ impl EarlyLintPass for AlmostCompleteRange {
9797
}
9898
}
9999

100-
extract_msrv_attr!(EarlyContext);
100+
extract_msrv_attr!();
101101
}
102102

103103
fn is_incomplete_range(start: &Expr, end: &Expr) -> bool {

clippy_lints/src/approx_const.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ pub struct ApproxConstant {
6868

6969
impl ApproxConstant {
7070
pub fn new(conf: &'static Conf) -> Self {
71-
Self {
72-
msrv: conf.msrv.clone(),
73-
}
71+
Self { msrv: conf.msrv }
7472
}
7573

7674
fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
@@ -91,7 +89,7 @@ impl ApproxConstant {
9189
let s = s.as_str();
9290
if s.parse::<f64>().is_ok() {
9391
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
94-
if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(msrv)) {
92+
if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(cx, msrv)) {
9593
span_lint_and_help(
9694
cx,
9795
APPROX_CONSTANT,
@@ -115,8 +113,6 @@ impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
115113
self.check_lit(cx, &lit.node, e);
116114
}
117115
}
118-
119-
extract_msrv_attr!(LateContext);
120116
}
121117

122118
/// Returns `false` if the number of significant figures in `value` are

clippy_lints/src/assigning_clones.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ pub struct AssigningClones {
5959

6060
impl AssigningClones {
6161
pub fn new(conf: &'static Conf) -> Self {
62-
Self {
63-
msrv: conf.msrv.clone(),
64-
}
62+
Self { msrv: conf.msrv }
6563
}
6664
}
6765

@@ -90,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
9088
sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone,
9189
_ if fn_name.as_str() == "to_owned"
9290
&& is_diag_trait_item(cx, fn_id, sym::ToOwned)
93-
&& self.msrv.meets(msrvs::CLONE_INTO) =>
91+
&& self.msrv.meets(cx, msrvs::CLONE_INTO) =>
9492
{
9593
CloneTrait::ToOwned
9694
},
@@ -143,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
143141
);
144142
}
145143
}
146-
147-
extract_msrv_attr!(LateContext);
148144
}
149145

150146
/// Checks if the data being cloned borrows from the place that is being assigned to:

clippy_lints/src/attrs/deprecated_cfg_attr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
22
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::msrvs::{self, MsrvStack};
44
use rustc_ast::AttrStyle;
55
use rustc_errors::Applicability;
66
use rustc_lint::EarlyContext;
77
use rustc_span::sym;
88

9-
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
9+
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
1010
// check cfg_attr
1111
if attr.has_name(sym::cfg_attr)
1212
&& let Some(items) = attr.meta_item_list()

0 commit comments

Comments
 (0)