Sync and Release automation (#13694)
Based on https://github.com/rust-lang/rust-clippy/pull/13693 Adds 2 subcommands to `cargo dev`: - `cargo dev sync update_nightly`: Which updates the nightly versions in `rust-toolchain` and `clippy_utils/README.md` - `cargo dev release bump_version`: Bumps the version in all relevant `Cargo.toml` files Those are pulled out of https://github.com/rust-lang/rust-clippy/pull/12759, which I'll rebase on this. Next step is to update the documentation, which I'll partly pull out of https://github.com/rust-lang/rust-clippy/pull/12762 r? @blyxyas (as you reviewed the first PR in the chain and were assigned to the second one) cc https://github.com/rust-lang/rust-clippy/issues/13556 changelog: none
This commit is contained in:
commit
a19d69d273
16 changed files with 263 additions and 154 deletions
|
@ -1,6 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
|
# begin autogenerated version
|
||||||
version = "0.1.84"
|
version = "0.1.84"
|
||||||
|
# end autogenerated version
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_config"
|
name = "clippy_config"
|
||||||
|
# begin autogenerated version
|
||||||
version = "0.1.84"
|
version = "0.1.84"
|
||||||
|
# end autogenerated version
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.0"
|
aho-corasick = "1.0"
|
||||||
|
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
indoc = "1.0"
|
indoc = "1.0"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{clippy_project_root, exit_if_err};
|
use crate::utils::{clippy_project_root, exit_if_err};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::clippy_project_root;
|
use crate::utils::clippy_project_root;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_lexer::{TokenKind, tokenize};
|
use rustc_lexer::{TokenKind, tokenize};
|
||||||
use shell_escape::escape;
|
use shell_escape::escape;
|
||||||
|
|
|
@ -14,69 +14,13 @@
|
||||||
extern crate rustc_driver;
|
extern crate rustc_driver;
|
||||||
extern crate rustc_lexer;
|
extern crate rustc_lexer;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::{self, ExitStatus};
|
|
||||||
|
|
||||||
pub mod dogfood;
|
pub mod dogfood;
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
pub mod lint;
|
pub mod lint;
|
||||||
pub mod new_lint;
|
pub mod new_lint;
|
||||||
|
pub mod release;
|
||||||
pub mod serve;
|
pub mod serve;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
|
pub mod sync;
|
||||||
pub mod update_lints;
|
pub mod update_lints;
|
||||||
|
pub mod utils;
|
||||||
#[cfg(not(windows))]
|
|
||||||
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
|
|
||||||
#[cfg(windows)]
|
|
||||||
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
|
|
||||||
|
|
||||||
/// Returns the path to the `cargo-clippy` binary
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the path of current executable could not be retrieved.
|
|
||||||
#[must_use]
|
|
||||||
pub fn cargo_clippy_path() -> PathBuf {
|
|
||||||
let mut path = std::env::current_exe().expect("failed to get current executable name");
|
|
||||||
path.set_file_name(CARGO_CLIPPY_EXE);
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path to the Clippy project directory
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the current directory could not be retrieved, there was an error reading any of the
|
|
||||||
/// Cargo.toml files or ancestor directory is the clippy root directory
|
|
||||||
#[must_use]
|
|
||||||
pub fn clippy_project_root() -> PathBuf {
|
|
||||||
let current_dir = std::env::current_dir().unwrap();
|
|
||||||
for path in current_dir.ancestors() {
|
|
||||||
let result = std::fs::read_to_string(path.join("Cargo.toml"));
|
|
||||||
if let Err(err) = &result {
|
|
||||||
if err.kind() == io::ErrorKind::NotFound {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = result.unwrap();
|
|
||||||
if content.contains("[package]\nname = \"clippy\"") {
|
|
||||||
return path.to_path_buf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if given command result was failed.
|
|
||||||
pub fn exit_if_err(status: io::Result<ExitStatus>) {
|
|
||||||
match status.expect("failed to run command").code() {
|
|
||||||
Some(0) => {},
|
|
||||||
Some(n) => process::exit(n),
|
|
||||||
None => {
|
|
||||||
eprintln!("Killed by signal");
|
|
||||||
process::exit(1);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{cargo_clippy_path, exit_if_err};
|
use crate::utils::{cargo_clippy_path, exit_if_err};
|
||||||
use std::process::{self, Command};
|
use std::process::{self, Command};
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -23,9 +23,9 @@ fn main() {
|
||||||
if print_only {
|
if print_only {
|
||||||
update_lints::print_lints();
|
update_lints::print_lints();
|
||||||
} else if check {
|
} else if check {
|
||||||
update_lints::update(update_lints::UpdateMode::Check);
|
update_lints::update(utils::UpdateMode::Check);
|
||||||
} else {
|
} else {
|
||||||
update_lints::update(update_lints::UpdateMode::Change);
|
update_lints::update(utils::UpdateMode::Change);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DevCommand::NewLint {
|
DevCommand::NewLint {
|
||||||
|
@ -35,7 +35,7 @@ fn main() {
|
||||||
r#type,
|
r#type,
|
||||||
msrv,
|
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(update_lints::UpdateMode::Change),
|
Ok(()) => update_lints::update(utils::UpdateMode::Change),
|
||||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||||
},
|
},
|
||||||
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||||
|
@ -75,6 +75,12 @@ fn main() {
|
||||||
uplift,
|
uplift,
|
||||||
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
|
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
|
||||||
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
|
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
|
||||||
|
DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
|
||||||
|
SyncSubcommand::UpdateNightly => sync::update_nightly(),
|
||||||
|
},
|
||||||
|
DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand {
|
||||||
|
ReleaseSubcommand::BumpVersion => release::bump_version(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,6 +231,10 @@ enum DevCommand {
|
||||||
/// The reason for deprecation
|
/// The reason for deprecation
|
||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
|
/// Sync between the rust repo and the Clippy repo
|
||||||
|
Sync(SyncCommand),
|
||||||
|
/// Manage Clippy releases
|
||||||
|
Release(ReleaseCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
@ -291,3 +301,29 @@ enum RemoveSubcommand {
|
||||||
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
||||||
VscodeTasks,
|
VscodeTasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct SyncCommand {
|
||||||
|
#[command(subcommand)]
|
||||||
|
subcommand: SyncSubcommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum SyncSubcommand {
|
||||||
|
#[command(name = "update_nightly")]
|
||||||
|
/// Update nightly version in rust-toolchain and `clippy_utils`
|
||||||
|
UpdateNightly,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct ReleaseCommand {
|
||||||
|
#[command(subcommand)]
|
||||||
|
subcommand: ReleaseSubcommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum ReleaseSubcommand {
|
||||||
|
#[command(name = "bump_version")]
|
||||||
|
/// Bump the version in the Cargo.toml files
|
||||||
|
BumpVersion,
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::clippy_project_root;
|
use crate::utils::{clippy_project_root, clippy_version};
|
||||||
use indoc::{formatdoc, writedoc};
|
use indoc::{formatdoc, writedoc};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
@ -186,23 +186,8 @@ fn to_camel_case(name: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_stabilization_version() -> String {
|
pub(crate) fn get_stabilization_version() -> String {
|
||||||
fn parse_manifest(contents: &str) -> Option<String> {
|
let (minor, patch) = clippy_version();
|
||||||
let version = contents
|
format!("{minor}.{patch}.0")
|
||||||
.lines()
|
|
||||||
.filter_map(|l| l.split_once('='))
|
|
||||||
.find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
|
|
||||||
let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let (minor, patch) = version.split_once('.')?;
|
|
||||||
Some(format!(
|
|
||||||
"{}.{}.0",
|
|
||||||
minor.parse::<u32>().ok()?,
|
|
||||||
patch.parse::<u32>().ok()?
|
|
||||||
))
|
|
||||||
}
|
|
||||||
let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
|
|
||||||
parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
|
fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
|
||||||
|
|
27
clippy_dev/src/release.rs
Normal file
27
clippy_dev/src/release.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::utils::{UpdateMode, clippy_version, replace_region_in_file};
|
||||||
|
|
||||||
|
const CARGO_TOML_FILES: [&str; 4] = [
|
||||||
|
"clippy_config/Cargo.toml",
|
||||||
|
"clippy_lints/Cargo.toml",
|
||||||
|
"clippy_utils/Cargo.toml",
|
||||||
|
"Cargo.toml",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn bump_version() {
|
||||||
|
let (minor, mut patch) = clippy_version();
|
||||||
|
patch += 1;
|
||||||
|
for file in &CARGO_TOML_FILES {
|
||||||
|
replace_region_in_file(
|
||||||
|
UpdateMode::Change,
|
||||||
|
Path::new(file),
|
||||||
|
"# begin autogenerated version\n",
|
||||||
|
"# end autogenerated version",
|
||||||
|
|res| {
|
||||||
|
writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
clippy_dev/src/sync.rs
Normal file
33
clippy_dev/src/sync.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use chrono::offset::Utc;
|
||||||
|
|
||||||
|
use crate::utils::{UpdateMode, replace_region_in_file};
|
||||||
|
|
||||||
|
pub fn update_nightly() {
|
||||||
|
// Update rust-toolchain nightly version
|
||||||
|
let date = Utc::now().format("%Y-%m-%d").to_string();
|
||||||
|
replace_region_in_file(
|
||||||
|
UpdateMode::Change,
|
||||||
|
Path::new("rust-toolchain"),
|
||||||
|
"# begin autogenerated nightly\n",
|
||||||
|
"# end autogenerated nightly",
|
||||||
|
|res| {
|
||||||
|
writeln!(res, "channel = \"nightly-{date}\"").unwrap();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update clippy_utils nightly version
|
||||||
|
replace_region_in_file(
|
||||||
|
UpdateMode::Change,
|
||||||
|
Path::new("clippy_utils/README.md"),
|
||||||
|
"<!-- begin autogenerated nightly -->\n",
|
||||||
|
"<!-- end autogenerated nightly -->",
|
||||||
|
|res| {
|
||||||
|
writeln!(res, "```").unwrap();
|
||||||
|
writeln!(res, "nightly-{date}").unwrap();
|
||||||
|
writeln!(res, "```").unwrap();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::clippy_project_root;
|
use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file};
|
||||||
use aho_corasick::AhoCorasickBuilder;
|
use aho_corasick::AhoCorasickBuilder;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
|
use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
|
||||||
|
@ -17,12 +17,6 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u
|
||||||
|
|
||||||
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum UpdateMode {
|
|
||||||
Check,
|
|
||||||
Change,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs the `update_lints` command.
|
/// Runs the `update_lints` command.
|
||||||
///
|
///
|
||||||
/// This updates various generated values from the lint source code.
|
/// This updates various generated values from the lint source code.
|
||||||
|
@ -511,14 +505,6 @@ fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_with_failure() {
|
|
||||||
println!(
|
|
||||||
"Not all lints defined properly. \
|
|
||||||
Please run `cargo dev update_lints` to make sure all lints are defined properly."
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lint data parsed from the Clippy source code.
|
/// Lint data parsed from the Clippy source code.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
struct Lint {
|
struct Lint {
|
||||||
|
@ -851,61 +837,6 @@ fn remove_line_splices(s: &str) -> String {
|
||||||
});
|
});
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces a region in a file delimited by two lines matching regexes.
|
|
||||||
///
|
|
||||||
/// `path` is the relative path to the file on which you want to perform the replacement.
|
|
||||||
///
|
|
||||||
/// See `replace_region_in_text` for documentation of the other options.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the path could not read or then written
|
|
||||||
fn replace_region_in_file(
|
|
||||||
update_mode: UpdateMode,
|
|
||||||
path: &Path,
|
|
||||||
start: &str,
|
|
||||||
end: &str,
|
|
||||||
write_replacement: impl FnMut(&mut String),
|
|
||||||
) {
|
|
||||||
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
|
|
||||||
let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match update_mode {
|
|
||||||
UpdateMode::Check if contents != new_contents => exit_with_failure(),
|
|
||||||
UpdateMode::Check => (),
|
|
||||||
UpdateMode::Change => {
|
|
||||||
if let Err(e) = fs::write(path, new_contents.as_bytes()) {
|
|
||||||
panic!("Cannot write to `{}`: {e}", path.display());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
|
|
||||||
/// were found, or the missing delimiter if not.
|
|
||||||
fn replace_region_in_text<'a>(
|
|
||||||
text: &str,
|
|
||||||
start: &'a str,
|
|
||||||
end: &'a str,
|
|
||||||
mut write_replacement: impl FnMut(&mut String),
|
|
||||||
) -> Result<String, &'a str> {
|
|
||||||
let (text_start, rest) = text.split_once(start).ok_or(start)?;
|
|
||||||
let (_, text_end) = rest.split_once(end).ok_or(end)?;
|
|
||||||
|
|
||||||
let mut res = String::with_capacity(text.len() + 4096);
|
|
||||||
res.push_str(text_start);
|
|
||||||
res.push_str(start);
|
|
||||||
write_replacement(&mut res);
|
|
||||||
res.push_str(end);
|
|
||||||
res.push_str(text_end);
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
||||||
match OpenOptions::new().create_new(true).write(true).open(new_name) {
|
match OpenOptions::new().create_new(true).write(true).open(new_name) {
|
||||||
Ok(file) => drop(file),
|
Ok(file) => drop(file),
|
||||||
|
|
142
clippy_dev/src/utils.rs
Normal file
142
clippy_dev/src/utils.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::{self, ExitStatus};
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
|
||||||
|
#[cfg(windows)]
|
||||||
|
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
|
||||||
|
|
||||||
|
/// Returns the path to the `cargo-clippy` binary
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the path of current executable could not be retrieved.
|
||||||
|
#[must_use]
|
||||||
|
pub fn cargo_clippy_path() -> PathBuf {
|
||||||
|
let mut path = std::env::current_exe().expect("failed to get current executable name");
|
||||||
|
path.set_file_name(CARGO_CLIPPY_EXE);
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path to the Clippy project directory
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the current directory could not be retrieved, there was an error reading any of the
|
||||||
|
/// Cargo.toml files or ancestor directory is the clippy root directory
|
||||||
|
#[must_use]
|
||||||
|
pub fn clippy_project_root() -> PathBuf {
|
||||||
|
let current_dir = std::env::current_dir().unwrap();
|
||||||
|
for path in current_dir.ancestors() {
|
||||||
|
let result = fs::read_to_string(path.join("Cargo.toml"));
|
||||||
|
if let Err(err) = &result {
|
||||||
|
if err.kind() == io::ErrorKind::NotFound {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = result.unwrap();
|
||||||
|
if content.contains("[package]\nname = \"clippy\"") {
|
||||||
|
return path.to_path_buf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if given command result was failed.
|
||||||
|
pub fn exit_if_err(status: io::Result<ExitStatus>) {
|
||||||
|
match status.expect("failed to run command").code() {
|
||||||
|
Some(0) => {},
|
||||||
|
Some(n) => process::exit(n),
|
||||||
|
None => {
|
||||||
|
eprintln!("Killed by signal");
|
||||||
|
process::exit(1);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clippy_version() -> (u32, u32) {
|
||||||
|
fn parse_manifest(contents: &str) -> Option<(u32, u32)> {
|
||||||
|
let version = contents
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.split_once('='))
|
||||||
|
.find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
|
||||||
|
let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let (minor, patch) = version.split_once('.')?;
|
||||||
|
Some((minor.parse().ok()?, patch.parse().ok()?))
|
||||||
|
}
|
||||||
|
let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
|
||||||
|
parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum UpdateMode {
|
||||||
|
Check,
|
||||||
|
Change,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exit_with_failure() {
|
||||||
|
println!(
|
||||||
|
"Not all lints defined properly. \
|
||||||
|
Please run `cargo dev update_lints` to make sure all lints are defined properly."
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces a region in a file delimited by two lines matching regexes.
|
||||||
|
///
|
||||||
|
/// `path` is the relative path to the file on which you want to perform the replacement.
|
||||||
|
///
|
||||||
|
/// See `replace_region_in_text` for documentation of the other options.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the path could not read or then written
|
||||||
|
pub(crate) fn replace_region_in_file(
|
||||||
|
update_mode: UpdateMode,
|
||||||
|
path: &Path,
|
||||||
|
start: &str,
|
||||||
|
end: &str,
|
||||||
|
write_replacement: impl FnMut(&mut String),
|
||||||
|
) {
|
||||||
|
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
|
||||||
|
let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match update_mode {
|
||||||
|
UpdateMode::Check if contents != new_contents => exit_with_failure(),
|
||||||
|
UpdateMode::Check => (),
|
||||||
|
UpdateMode::Change => {
|
||||||
|
if let Err(e) = fs::write(path, new_contents.as_bytes()) {
|
||||||
|
panic!("Cannot write to `{}`: {e}", path.display());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
|
||||||
|
/// were found, or the missing delimiter if not.
|
||||||
|
pub(crate) fn replace_region_in_text<'a>(
|
||||||
|
text: &str,
|
||||||
|
start: &'a str,
|
||||||
|
end: &'a str,
|
||||||
|
mut write_replacement: impl FnMut(&mut String),
|
||||||
|
) -> Result<String, &'a str> {
|
||||||
|
let (text_start, rest) = text.split_once(start).ok_or(start)?;
|
||||||
|
let (_, text_end) = rest.split_once(end).ok_or(end)?;
|
||||||
|
|
||||||
|
let mut res = String::with_capacity(text.len() + 4096);
|
||||||
|
res.push_str(text_start);
|
||||||
|
res.push_str(start);
|
||||||
|
write_replacement(&mut res);
|
||||||
|
res.push_str(end);
|
||||||
|
res.push_str(text_end);
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
|
# begin autogenerated version
|
||||||
version = "0.1.84"
|
version = "0.1.84"
|
||||||
|
# end autogenerated version
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_utils"
|
name = "clippy_utils"
|
||||||
|
# begin autogenerated version
|
||||||
version = "0.1.84"
|
version = "0.1.84"
|
||||||
|
# end autogenerated version
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Helpful tools for writing lints, provided as they are used in Clippy"
|
description = "Helpful tools for writing lints, provided as they are used in Clippy"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
|
# begin autogenerated nightly
|
||||||
channel = "nightly-2024-11-14"
|
channel = "nightly-2024-11-14"
|
||||||
|
# end autogenerated nightly
|
||||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue