about summary refs log tree commit diff
path: root/clippy_dev
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2025-02-28 23:20:48 +0100
committerPhilipp Krones <hello@philkrones.com>2025-02-28 23:27:09 +0100
commitfe01c4499507fc6ba21e07ff9824c14443b308e9 (patch)
tree48eb83134a7122ddbecda437dfb8b558baf5cb8f /clippy_dev
parent222aaba5a18a27f747573aa73d07d7fedfff0231 (diff)
downloadrust-fe01c4499507fc6ba21e07ff9824c14443b308e9.tar.gz
rust-fe01c4499507fc6ba21e07ff9824c14443b308e9.zip
Merge commit '9f9a822509e5ad3e560cbbe830d1013f936fca28' into clippy-subtree-update
Diffstat (limited to 'clippy_dev')
-rw-r--r--clippy_dev/src/dogfood.rs7
-rw-r--r--clippy_dev/src/fmt.rs9
-rw-r--r--clippy_dev/src/lint.rs4
-rw-r--r--clippy_dev/src/main.rs37
-rw-r--r--clippy_dev/src/new_lint.rs85
-rw-r--r--clippy_dev/src/setup/toolchain.rs27
6 files changed, 114 insertions, 55 deletions
diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs
index 75a4cbd2f92..05fa24d8d4e 100644
--- a/clippy_dev/src/dogfood.rs
+++ b/clippy_dev/src/dogfood.rs
@@ -4,7 +4,8 @@ use std::process::Command;
 /// # Panics
 ///
 /// Panics if unable to run the dogfood test
-pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
+#[allow(clippy::fn_params_excessive_bools)]
+pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) {
     let mut cmd = Command::new("cargo");
 
     cmd.current_dir(clippy_project_root())
@@ -25,6 +26,10 @@ pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
         dogfood_args.push("--allow-staged");
     }
 
+    if allow_no_vcs {
+        dogfood_args.push("--allow-no-vcs");
+    }
+
     cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
 
     exit_if_err(cmd.status());
diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs
index 790dafa811f..bdddf46a2cb 100644
--- a/clippy_dev/src/fmt.rs
+++ b/clippy_dev/src/fmt.rs
@@ -290,8 +290,13 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
         .filter_map(|entry| {
             let entry = entry.expect("failed to find tests");
             let path = entry.path();
-
-            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+            if path.extension() != Some("rs".as_ref())
+                || path
+                    .components()
+                    .nth_back(1)
+                    .is_some_and(|c| c.as_os_str() == "syntax-error-recovery")
+                || entry.file_name() == "ice-3891.rs"
+            {
                 None
             } else {
                 Some(entry.into_path().into_os_string())
diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs
index 125195397e6..e0e036757d5 100644
--- a/clippy_dev/src/lint.rs
+++ b/clippy_dev/src/lint.rs
@@ -2,7 +2,7 @@ use crate::utils::{cargo_clippy_path, exit_if_err};
 use std::process::{self, Command};
 use std::{env, fs};
 
-pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
+pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>) {
     let is_file = match fs::metadata(path) {
         Ok(metadata) => metadata.is_file(),
         Err(e) => {
@@ -17,7 +17,7 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
                 .args(["run", "--bin", "clippy-driver", "--"])
                 .args(["-L", "./target/debug"])
                 .args(["-Z", "no-codegen"])
-                .args(["--edition", "2021"])
+                .args(["--edition", edition])
                 .arg(path)
                 .args(args)
                 // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 56ed60256f1..074dea4ab77 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -17,7 +17,8 @@ fn main() {
             fix,
             allow_dirty,
             allow_staged,
-        } => dogfood::dogfood(fix, allow_dirty, allow_staged),
+            allow_no_vcs,
+        } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
         DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
         DevCommand::UpdateLints { print_only, check } => {
             if print_only {
@@ -34,7 +35,7 @@ fn main() {
             category,
             r#type,
             msrv,
-        } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
+        } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) {
             Ok(()) => update_lints::update(utils::UpdateMode::Change),
             Err(e) => eprintln!("Unable to create lint: {e}"),
         },
@@ -53,7 +54,12 @@ fn main() {
                     setup::git_hook::install_hook(force_override);
                 }
             },
-            SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name),
+            SetupSubcommand::Toolchain {
+                standalone,
+                force,
+                release,
+                name,
+            } => setup::toolchain::create(standalone, force, release, &name),
             SetupSubcommand::VscodeTasks { remove, force_override } => {
                 if remove {
                     setup::vscode::remove_tasks();
@@ -68,7 +74,7 @@ fn main() {
             RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
         },
         DevCommand::Serve { port, lint } => serve::run(port, lint),
-        DevCommand::Lint { path, args } => lint::run(&path, args.iter()),
+        DevCommand::Lint { path, edition, args } => lint::run(&path, &edition, args.iter()),
         DevCommand::RenameLint {
             old_name,
             new_name,
@@ -106,6 +112,9 @@ enum DevCommand {
         #[arg(long, requires = "fix")]
         /// Fix code even if the working directory has staged changes
         allow_staged: bool,
+        #[arg(long, requires = "fix")]
+        /// Fix code even if a VCS was not detected
+        allow_no_vcs: bool,
     },
     /// Run rustfmt on all projects and tests
     Fmt {
@@ -138,9 +147,9 @@ enum DevCommand {
     #[command(name = "new_lint")]
     /// Create a new lint and run `cargo dev update_lints`
     NewLint {
-        #[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
+        #[arg(short, long, conflicts_with = "type", default_value = "late")]
         /// Specify whether the lint runs during the early or late pass
-        pass: String,
+        pass: new_lint::Pass,
         #[arg(
             short,
             long,
@@ -206,6 +215,9 @@ enum DevCommand {
     ///     cargo dev lint file.rs -- -W clippy::pedantic {n}
     ///     cargo dev lint ~/my-project -- -- -W clippy::pedantic
     Lint {
+        /// The Rust edition to use
+        #[arg(long, default_value = "2024")]
+        edition: String,
         /// The path to a file or package directory to lint
         path: String,
         /// Pass extra arguments to cargo/clippy-driver
@@ -264,14 +276,25 @@ enum SetupSubcommand {
         force_override: bool,
     },
     /// Install a rustup toolchain pointing to the local clippy build
+    ///
+    /// This creates a toolchain with symlinks pointing at
+    /// `target/.../{clippy-driver,cargo-clippy}`, rebuilds of the project will be reflected in the
+    /// created toolchain unless `--standalone` is passed
     Toolchain {
         #[arg(long, short)]
+        /// Create a standalone toolchain by copying the clippy binaries instead
+        /// of symlinking them
+        ///
+        /// Use this for example to create a toolchain, make a small change and then make another
+        /// toolchain with a different name in order to easily compare the two
+        standalone: bool,
+        #[arg(long, short)]
         /// Override an existing toolchain
         force: bool,
         #[arg(long, short)]
         /// Point to --release clippy binary
         release: bool,
-        #[arg(long, default_value = "clippy")]
+        #[arg(long, short, default_value = "clippy")]
         /// Name of the toolchain
         name: String,
     },
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index cc4b26867a2..96e12706c9e 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -1,14 +1,28 @@
 use crate::utils::{clippy_project_root, clippy_version};
+use clap::ValueEnum;
 use indoc::{formatdoc, writedoc};
-use std::fmt;
-use std::fmt::Write as _;
+use std::fmt::{self, Write as _};
 use std::fs::{self, OpenOptions};
-use std::io::prelude::*;
-use std::io::{self, ErrorKind};
+use std::io::{self, Write as _};
 use std::path::{Path, PathBuf};
 
+#[derive(Clone, Copy, PartialEq, ValueEnum)]
+pub enum Pass {
+    Early,
+    Late,
+}
+
+impl fmt::Display for Pass {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(match self {
+            Pass::Early => "early",
+            Pass::Late => "late",
+        })
+    }
+}
+
 struct LintData<'a> {
-    pass: &'a str,
+    pass: Pass,
     name: &'a str,
     category: &'a str,
     ty: Option<&'a str>,
@@ -25,7 +39,7 @@ impl<T> Context for io::Result<T> {
             Ok(t) => Ok(t),
             Err(e) => {
                 let message = format!("{}: {e}", text.as_ref());
-                Err(io::Error::new(ErrorKind::Other, message))
+                Err(io::Error::other(message))
             },
         }
     }
@@ -36,7 +50,7 @@ impl<T> Context for io::Result<T> {
 /// # Errors
 ///
 /// This function errors out if the files couldn't be created or written to.
-pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
+pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
     if category == "cargo" && ty.is_none() {
         // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
         ty = Some("cargo");
@@ -57,7 +71,7 @@ pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv
         add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
     }
 
-    if pass == "early" {
+    if pass == Pass::Early {
         println!(
             "\n\
             NOTE: Use a late pass unless you need something specific from\n\
@@ -137,23 +151,17 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
     let mut lib_rs = fs::read_to_string(path).context("reading")?;
 
     let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
+    let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
+    let lint_pass = lint.pass;
+    let module_name = lint.name;
+    let camel_name = to_camel_case(lint.name);
 
     let new_lint = if enable_msrv {
         format!(
             "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n    ",
-            lint_pass = lint.pass,
-            ctor_arg = if lint.pass == "late" { "_" } else { "" },
-            module_name = lint.name,
-            camel_name = to_camel_case(lint.name),
         )
     } else {
-        format!(
-            "store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n    ",
-            lint_pass = lint.pass,
-            ctor_arg = if lint.pass == "late" { "_" } else { "" },
-            module_name = lint.name,
-            camel_name = to_camel_case(lint.name),
-        )
+        format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n    ",)
     };
 
     lib_rs.insert_str(comment_start, &new_lint);
@@ -243,11 +251,16 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     let mut result = String::new();
 
     let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
-        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
-        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
-        _ => {
-            unreachable!("`pass_type` should only ever be `early` or `late`!");
-        },
+        Pass::Early => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
+        Pass::Late => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
+    };
+    let (msrv_ty, msrv_ctor, extract_msrv) = match lint.pass {
+        Pass::Early => (
+            "MsrvStack",
+            "MsrvStack::new(conf.msrv)",
+            "\n    extract_msrv_attr!();\n",
+        ),
+        Pass::Late => ("Msrv", "conf.msrv", ""),
     };
 
     let lint_name = lint.name;
@@ -259,10 +272,10 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         let _: fmt::Result = writedoc!(
             result,
             r"
-            use clippy_utils::msrvs::{{self, Msrv}};
+            use clippy_utils::msrvs::{{self, {msrv_ty}}};
             use clippy_config::Conf;
             {pass_import}
-            use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
+            use rustc_lint::{{{context_import}, {pass_type}}};
             use rustc_session::impl_lint_pass;
 
         "
@@ -286,20 +299,18 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
             result,
             r"
             pub struct {name_camel} {{
-                msrv: Msrv,
+                msrv: {msrv_ty},
             }}
 
             impl {name_camel} {{
                 pub fn new(conf: &'static Conf) -> Self {{
-                    Self {{ msrv: conf.msrv.clone() }}
+                    Self {{ msrv: {msrv_ctor} }}
                 }}
             }}
 
             impl_lint_pass!({name_camel} => [{name_upper}]);
 
-            impl {pass_type}{pass_lifetimes} for {name_camel} {{
-                extract_msrv_attr!({context_import});
-            }}
+            impl {pass_type}{pass_lifetimes} for {name_camel} {{{extract_msrv}}}
 
             // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
             // TODO: Update msrv config comment in `clippy_config/src/conf.rs`
@@ -376,9 +387,9 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
 
     let mod_file_path = ty_dir.join("mod.rs");
     let context_import = setup_mod_file(&mod_file_path, lint)?;
-    let pass_lifetimes = match context_import {
-        "LateContext" => "<'_>",
-        _ => "",
+    let (pass_lifetimes, msrv_ty, msrv_ref, msrv_cx) = match context_import {
+        "LateContext" => ("<'_>", "Msrv", "", "cx, "),
+        _ => ("", "MsrvStack", "&", ""),
     };
 
     let name_upper = lint.name.to_uppercase();
@@ -388,14 +399,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
         let _: fmt::Result = writedoc!(
             lint_file_contents,
             r#"
-                use clippy_utils::msrvs::{{self, Msrv}};
+                use clippy_utils::msrvs::{{self, {msrv_ty}}};
                 use rustc_lint::{{{context_import}, LintContext}};
 
                 use super::{name_upper};
 
                 // TODO: Adjust the parameters as necessary
-                pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: &Msrv) {{
-                    if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
+                pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: {msrv_ref}{msrv_ty}) {{
+                    if !msrv.meets({msrv_cx}todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
                         return;
                     }}
                     todo!();
diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs
index 8d98c6c92d9..2966629cf70 100644
--- a/clippy_dev/src/setup/toolchain.rs
+++ b/clippy_dev/src/setup/toolchain.rs
@@ -3,11 +3,14 @@ use std::env::current_dir;
 use std::ffi::OsStr;
 use std::fs;
 use std::path::{Path, PathBuf};
+use std::process::Command;
 use walkdir::WalkDir;
 
+use crate::utils::exit_if_err;
+
 use super::verify_inside_clippy_dir;
 
-pub fn create(force: bool, release: bool, name: &str) {
+pub fn create(standalone: bool, force: bool, release: bool, name: &str) {
     if !verify_inside_clippy_dir() {
         return;
     }
@@ -48,14 +51,22 @@ pub fn create(force: bool, release: bool, name: &str) {
         }
     }
 
-    symlink_bin("cargo-clippy", &dest, release);
-    symlink_bin("clippy-driver", &dest, release);
+    let status = Command::new("cargo")
+        .arg("build")
+        .args(release.then_some("--release"))
+        .status();
+    exit_if_err(status);
+
+    install_bin("cargo-clippy", &dest, standalone, release);
+    install_bin("clippy-driver", &dest, standalone, release);
 
     println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
-    println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
+    if !standalone {
+        println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
+    }
 }
 
-fn symlink_bin(bin: &str, dest: &Path, release: bool) {
+fn install_bin(bin: &str, dest: &Path, standalone: bool, release: bool) {
     #[cfg(windows)]
     use std::os::windows::fs::symlink_file as symlink;
 
@@ -71,5 +82,9 @@ fn symlink_bin(bin: &str, dest: &Path, release: bool) {
     let mut dest = dest.to_path_buf();
     dest.extend(["bin", &file_name]);
 
-    symlink(src, dest).unwrap();
+    if standalone {
+        fs::copy(src, dest).unwrap();
+    } else {
+        symlink(src, dest).unwrap();
+    }
 }