clippy_dev: Rename deprecate_lint module to edit_lints.

This commit is contained in:
Jason Newcomb 2025-10-12 14:43:21 -04:00
parent 756e1bac6e
commit d9fa6d9515
3 changed files with 218 additions and 5 deletions

View file

@ -0,0 +1,213 @@
use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint};
use crate::update_lints::generate_lint_files;
use crate::utils::{UpdateMode, Version};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::{fs, io};
/// Runs the `deprecate` command
///
/// This does the following:
/// * Adds an entry to `deprecated_lints.rs`.
/// * Removes the lint declaration (and the entire file if applicable)
///
/// # Panics
///
/// If a file path could not read from or written to
pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) {
let mut lints = cx.find_lint_decls();
let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints();
let Some(lint) = lints.iter().find(|l| l.name == name) else {
eprintln!("error: failed to find lint `{name}`");
return;
};
let prefixed_name = cx.str_buf.with(|buf| {
buf.extend(["clippy::", name]);
cx.arena.alloc_str(buf)
});
match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) {
Ok(_) => {
println!("`{name}` is already deprecated");
return;
},
Err(idx) => deprecated_lints.insert(
idx,
DeprecatedLint {
name: prefixed_name,
reason,
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
},
),
}
let mod_path = {
let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
if mod_path.is_dir() {
mod_path = mod_path.join("mod");
}
mod_path.set_extension("rs");
mod_path
};
if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) {
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("info: `{name}` has successfully been deprecated");
println!("note: you must run `cargo uitest` to update the test results");
} else {
eprintln!("error: lint not found");
}
}
pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) {
let mut lints = cx.find_lint_decls();
let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints();
let Some(lint) = lints.iter().find(|l| l.name == old_name) else {
eprintln!("error: failed to find lint `{old_name}`");
return;
};
let old_name_prefixed = cx.str_buf.with(|buf| {
buf.extend(["clippy::", old_name]);
cx.arena.alloc_str(buf)
});
for lint in &mut renamed_lints {
if lint.new_name == old_name_prefixed {
lint.new_name = new_name;
}
}
match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) {
Ok(_) => {
println!("`{old_name}` is already deprecated");
return;
},
Err(idx) => renamed_lints.insert(
idx,
RenamedLint {
old_name: old_name_prefixed,
new_name,
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
},
),
}
let mod_path = {
let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
if mod_path.is_dir() {
mod_path = mod_path.join("mod");
}
mod_path.set_extension("rs");
mod_path
};
if remove_lint_declaration(old_name, &mod_path, &mut lints).unwrap_or(false) {
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("info: `{old_name}` has successfully been uplifted");
println!("note: you must run `cargo uitest` to update the test results");
} else {
eprintln!("error: lint not found");
}
}
fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint<'_>>) -> io::Result<bool> {
fn remove_lint(name: &str, lints: &mut Vec<Lint<'_>>) {
lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
}
fn remove_test_assets(name: &str) {
let test_file_stem = format!("tests/ui/{name}");
let path = Path::new(&test_file_stem);
// Some lints have their own directories, delete them
if path.is_dir() {
let _ = fs::remove_dir_all(path);
return;
}
// Remove all related test files
let _ = fs::remove_file(path.with_extension("rs"));
let _ = fs::remove_file(path.with_extension("stderr"));
let _ = fs::remove_file(path.with_extension("fixed"));
}
fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
content
.find("declare_lint_pass!")
.unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
});
let mut impl_lint_pass_end = content[impl_lint_pass_start..]
.find(']')
.expect("failed to find `impl_lint_pass` terminator");
impl_lint_pass_end += impl_lint_pass_start;
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
for c in content[lint_name_end..impl_lint_pass_end].chars() {
// Remove trailing whitespace
if c == ',' || c.is_whitespace() {
lint_name_end += 1;
} else {
break;
}
}
content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
}
}
if path.exists()
&& let Some(lint) = lints.iter().find(|l| l.name == name)
{
if lint.module == name {
// The lint name is the same as the file, we can just delete the entire file
fs::remove_file(path)?;
} else {
// We can't delete the entire file, just remove the declaration
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
// Remove clippy_lints/src/some_mod/some_lint.rs
let mut lint_mod_path = path.to_path_buf();
lint_mod_path.set_file_name(name);
lint_mod_path.set_extension("rs");
let _ = fs::remove_file(lint_mod_path);
}
let mut content =
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
eprintln!(
"warn: you will have to manually remove any code related to `{name}` from `{}`",
path.display()
);
assert!(
content[lint.declaration_range].contains(&name.to_uppercase()),
"error: `{}` does not contain lint `{}`'s declaration",
path.display(),
lint.name
);
// Remove lint declaration (declare_clippy_lint!)
content.replace_range(lint.declaration_range, "");
// Remove the module declaration (mod xyz;)
let mod_decl = format!("\nmod {name};");
content = content.replacen(&mod_decl, "", 1);
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
}
remove_test_assets(name);
remove_lint(name, lints);
return Ok(true);
}
Ok(false)
}

View file

@ -23,8 +23,8 @@ extern crate rustc_arena;
extern crate rustc_driver;
extern crate rustc_lexer;
pub mod deprecate_lint;
pub mod dogfood;
pub mod edit_lints;
pub mod fmt;
pub mod lint;
pub mod new_lint;

View file

@ -4,8 +4,8 @@
use clap::{Args, Parser, Subcommand};
use clippy_dev::{
ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve,
setup, sync, update_lints,
ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, setup,
sync, update_lints,
};
use std::env;
@ -78,10 +78,10 @@ fn main() {
rename_lint::rename(cx, clippy.version, &old_name, &new_name);
}),
DevCommand::Uplift { old_name, new_name } => new_parse_cx(|cx| {
deprecate_lint::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name));
edit_lints::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name));
}),
DevCommand::Deprecate { name, reason } => {
new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason));
new_parse_cx(|cx| edit_lints::deprecate(cx, clippy.version, &name, &reason));
},
DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
SyncSubcommand::UpdateNightly => sync::update_nightly(),