Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6304,6 +6304,7 @@ Released 2018-09-13
[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
[`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards
[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned
[`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ path = "src/driver.rs"
[dependencies]
clippy_config = { path = "clippy_config" }
clippy_lints = { path = "clippy_lints" }
clippy_lints_loops = { path = "clippy_lints_loops" }
clippy_lints_methods = { path = "clippy_lints_methods" }
clippy_utils = { path = "clippy_utils" }
declare_clippy_lint = { path = "declare_clippy_lint" }
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
Expand Down
1 change: 1 addition & 0 deletions clippy_dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
clap = { version = "4.4", features = ["derive"] }
indoc = "1.0"
itertools = "0.12"
memchr = "2.7.5"
opener = "0.7"
walkdir = "2.3"

Expand Down
179 changes: 79 additions & 100 deletions clippy_dev/src/deprecate_lint.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints};
use crate::utils::{UpdateMode, Version};
use crate::parse::{ActiveLint, DeprecatedLint, LintKind, ParsedData};
use crate::update_lints::generate_lint_files;
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists};
use core::mem;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::{fs, io};
use std::path::Path;

/// Runs the `deprecate` command
///
Expand All @@ -18,68 +19,42 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
panic!("`{name}` should not contain the `{prefix}` prefix");
}

let mut lints = find_lint_decls();
let (mut deprecated_lints, renamed_lints) = read_deprecated_lints();

let Some(lint) = lints.iter().find(|l| l.name == name) else {
let mut data = ParsedData::collect();
let Some(entry) = data.lints.get_mut(name) else {
eprintln!("error: failed to find lint `{name}`");
return;
};

let prefixed_name = String::from_iter(["clippy::", name]);
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: reason.into(),
version: clippy_version.rust_display().to_string(),
},
),
}

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
let LintKind::Active(lint) = mem::replace(
&mut entry.kind,
LintKind::Deprecated(DeprecatedLint {
reason: reason.into(),
version: clippy_version.rust_display().to_string(),
}),
) else {
eprintln!("error: lint `{name}` is already deprecated");
return;
};

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");
}
remove_lint_declaration(name, &lint, &data);
generate_lint_files(UpdateMode::Change, &data);
println!("info: `{name}` has successfully been deprecated");
println!("note: you must run `cargo uitest` to update the test results");
}

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_lint_declaration(name: &str, lint: &ActiveLint, data: &ParsedData) {
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;
delete_dir_if_exists(path);
} else {
// Remove all related test files
delete_file_if_exists(&path.with_extension("rs"));
delete_file_if_exists(&path.with_extension("stderr"));
delete_file_if_exists(&path.with_extension("fixed"));
}

// 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) {
Expand Down Expand Up @@ -108,54 +83,58 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
}
}

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)?;
let lint_file = &data.source_map.files[lint.decl_span.file];
if data.lints.values().any(|l| {
let other_file = &data.source_map.files[l.name_span.file];
other_file.krate == lint_file.krate && other_file.module.starts_with(&lint_file.module)
}) {
// Try to delete a sub-module that matches the lint's name
let removed_mod = if lint_file.path.file_name().map(OsStr::as_encoded_bytes) == Some(b"mod.rs") {
let mut path = lint_file.path.clone();
path.set_file_name(name);
path.set_extension("rs");
delete_file_if_exists(&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.clone()].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.clone(), "");

// 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);
false
};

FileUpdater::default().update_file(&lint_file.path, &mut |_, src, dst| {
let (a, b, c, d) = if removed_mod
&& let mod_decl = format!("\nmod {name};")
&& let Some(mod_start) = src.find(&mod_decl)
{
if mod_start < lint.decl_span.start as usize {
(
mod_start,
mod_start + mod_decl.len(),
lint.decl_span.start as usize,
lint.decl_span.end as usize,
)
} else {
(
lint.decl_span.start as usize,
lint.decl_span.end as usize,
mod_start,
mod_start + mod_decl.len(),
)
}
} else {
(
lint.decl_span.start as usize,
lint.decl_span.end as usize,
src.len(),
src.len(),
)
};
dst.push_str(&src[..a]);
dst.push_str(&src[b..c]);
dst.push_str(&src[d..]);
remove_impl_lint_pass(&name.to_uppercase(), dst);
UpdateStatus::Changed
});
} else {
// No other lint in the same module or a sub-module.
delete_file_if_exists(&lint_file.path);
}

Ok(false)
remove_test_assets(name);
}
2 changes: 1 addition & 1 deletion clippy_dev/src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ fn run_rustfmt(update_mode: UpdateMode) {
.expect("invalid rustfmt path");
rustfmt_path.truncate(rustfmt_path.trim_end().len());

let args: Vec<_> = walk_dir_no_dot_or_target()
let args: Vec<_> = walk_dir_no_dot_or_target(".")
.filter_map(|e| {
let e = expect_action(e, ErrAction::Read, ".");
e.path()
Expand Down
10 changes: 8 additions & 2 deletions clippy_dev/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![feature(
rustc_private,
array_windows,
exit_status_error,
if_let_guard,
let_chains,
Expand All @@ -16,9 +17,11 @@
)]
#![allow(clippy::missing_panics_doc)]

// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
#[allow(unused_extern_crates)]
#[expect(unused_extern_crates, reason = "needed to lint to rustc crates")]
extern crate rustc_driver;

extern crate rustc_data_structures;
extern crate rustc_index;
extern crate rustc_lexer;
extern crate rustc_literal_escaper;

Expand All @@ -34,3 +37,6 @@ pub mod setup;
pub mod sync;
pub mod update_lints;
pub mod utils;

mod parse;
mod source_map;
11 changes: 6 additions & 5 deletions clippy_dev/src/new_lint.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::utils::{RustSearcher, Token, Version};
use crate::parse::{Capture, RustSearcher, Token};
use crate::utils::Version;
use clap::ValueEnum;
use indoc::{formatdoc, writedoc};
use std::fmt::{self, Write as _};
Expand Down Expand Up @@ -522,17 +523,17 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) {
let mut context = None;
let mut decl_end = None;
let mut searcher = RustSearcher::new(contents);
while let Some(name) = searcher.find_capture_token(CaptureIdent) {
let mut captures = [Capture::EMPTY];
while let Some(name) = searcher.find_any_ident() {
match name {
"declare_clippy_lint" => {
if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) {
decl_end = Some(searcher.pos());
}
},
"impl" => {
let mut capture = "";
if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) {
match capture {
if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) {
match searcher.get_capture(captures[0]) {
"LateLintPass" => context = Some("LateContext"),
"EarlyLintPass" => context = Some("EarlyContext"),
_ => {},
Expand Down
Loading