mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-24 21:05:12 +00:00
Auto merge of #8871 - Serial-ATA:cargo-dev-deprecate, r=giraffate
Add `cargo dev deprecate` changelog: none I wrote this awhile ago when `regex` was still a dependency. Is it alright to add it back?
This commit is contained in:
commit
57e7e1d7d8
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "0.7"
|
||||
clap = "3.1"
|
||||
clap = "3.2"
|
||||
indoc = "1.0"
|
||||
itertools = "0.10.1"
|
||||
opener = "0.5"
|
||||
|
@ -5,6 +5,7 @@
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
|
||||
use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use indoc::indoc;
|
||||
|
||||
fn main() {
|
||||
let matches = get_clap_config();
|
||||
|
||||
@ -85,6 +86,11 @@ fn main() {
|
||||
let uplift = matches.contains_id("uplift");
|
||||
update_lints::rename(old_name, new_name, uplift);
|
||||
},
|
||||
Some(("deprecate", matches)) => {
|
||||
let name = matches.get_one::<String>("name").unwrap();
|
||||
let reason = matches.get_one("reason");
|
||||
update_lints::deprecate(name, reason);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -266,6 +272,18 @@ fn get_clap_config() -> ArgMatches {
|
||||
.long("uplift")
|
||||
.help("This lint will be uplifted into rustc"),
|
||||
]),
|
||||
Command::new("deprecate").about("Deprecates the given lint").args([
|
||||
Arg::new("name")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The name of the lint to deprecate"),
|
||||
Arg::new("reason")
|
||||
.long("reason")
|
||||
.short('r')
|
||||
.required(false)
|
||||
.takes_value(true)
|
||||
.help("The reason for deprecation"),
|
||||
]),
|
||||
])
|
||||
.get_matches()
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ fn to_camel_case(name: &str) -> String {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_stabilization_version() -> String {
|
||||
pub(crate) fn get_stabilization_version() -> String {
|
||||
fn parse_manifest(contents: &str) -> Option<String> {
|
||||
let version = contents
|
||||
.lines()
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::clippy_project_root;
|
||||
use aho_corasick::AhoCorasickBuilder;
|
||||
use core::fmt::Write as _;
|
||||
use indoc::writedoc;
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io::{self, Read as _, Seek as _, Write as _};
|
||||
use std::fmt::Write;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write as _};
|
||||
use std::ops::Range;
|
||||
use std::path::{Path, PathBuf};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::clippy_project_root;
|
||||
|
||||
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
|
||||
// Use that command to update this file and do not edit by hand.\n\
|
||||
// Manual edits will be overwritten.\n\n";
|
||||
@ -326,6 +327,200 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
|
||||
println!("note: `cargo uitest` still needs to be run to update the test results");
|
||||
}
|
||||
|
||||
const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
|
||||
/// 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(name: &str, reason: Option<&String>) {
|
||||
fn finish(
|
||||
(lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
|
||||
name: &str,
|
||||
reason: &str,
|
||||
) {
|
||||
deprecated_lints.push(DeprecatedLint {
|
||||
name: name.to_string(),
|
||||
reason: reason.to_string(),
|
||||
declaration_range: Range::default(),
|
||||
});
|
||||
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
println!("info: `{}` has successfully been deprecated", name);
|
||||
|
||||
if reason == DEFAULT_DEPRECATION_REASON {
|
||||
println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
|
||||
}
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
|
||||
let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
|
||||
let name_lower = name.to_lowercase();
|
||||
let name_upper = name.to_uppercase();
|
||||
|
||||
let (mut lints, deprecated_lints, renamed_lints) = gather_all();
|
||||
let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; };
|
||||
|
||||
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 deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
|
||||
|
||||
if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
|
||||
declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
|
||||
finish((lints, deprecated_lints, renamed_lints), name, reason);
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
fs::remove_dir_all(path).ok();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all related test files
|
||||
fs::remove_file(path.with_extension("rs")).ok();
|
||||
fs::remove_file(path.with_extension("stderr")).ok();
|
||||
fs::remove_file(path.with_extension("fixed")).ok();
|
||||
}
|
||||
|
||||
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() {
|
||||
if 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");
|
||||
|
||||
fs::remove_file(lint_mod_path).ok();
|
||||
}
|
||||
|
||||
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 `{}` from `{}`",
|
||||
name,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
|
||||
let mut file = OpenOptions::new().write(true).open(path)?;
|
||||
|
||||
file.seek(SeekFrom::End(0))?;
|
||||
|
||||
let version = crate::new_lint::get_stabilization_version();
|
||||
let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
|
||||
"TODO"
|
||||
} else {
|
||||
reason
|
||||
};
|
||||
|
||||
writedoc!(
|
||||
file,
|
||||
"
|
||||
|
||||
declare_deprecated_lint! {{
|
||||
/// ### What it does
|
||||
/// Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// ### Deprecation reason
|
||||
/// {}
|
||||
#[clippy::version = \"{}\"]
|
||||
pub {},
|
||||
\"{}\"
|
||||
}}
|
||||
|
||||
",
|
||||
deprecation_reason,
|
||||
version,
|
||||
name,
|
||||
reason,
|
||||
)
|
||||
}
|
||||
|
||||
/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
|
||||
/// were no replacements.
|
||||
fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
|
||||
@ -393,16 +588,18 @@ struct Lint {
|
||||
group: String,
|
||||
desc: String,
|
||||
module: String,
|
||||
declaration_range: Range<usize>,
|
||||
}
|
||||
|
||||
impl Lint {
|
||||
#[must_use]
|
||||
fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
|
||||
fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
|
||||
Self {
|
||||
name: name.to_lowercase(),
|
||||
group: group.into(),
|
||||
desc: remove_line_splices(desc),
|
||||
module: module.into(),
|
||||
declaration_range,
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,12 +630,14 @@ impl Lint {
|
||||
struct DeprecatedLint {
|
||||
name: String,
|
||||
reason: String,
|
||||
declaration_range: Range<usize>,
|
||||
}
|
||||
impl DeprecatedLint {
|
||||
fn new(name: &str, reason: &str) -> Self {
|
||||
fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
|
||||
Self {
|
||||
name: name.to_lowercase(),
|
||||
reason: remove_line_splices(reason),
|
||||
declaration_range,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,7 +809,11 @@ fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
|
||||
macro_rules! match_tokens {
|
||||
($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
|
||||
{
|
||||
$($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
|
||||
$($(let $capture =)? if let Some(LintDeclSearchResult {
|
||||
token_kind: TokenKind::$token $({$($fields)*})?,
|
||||
content: _x,
|
||||
..
|
||||
}) = $iter.next() {
|
||||
_x
|
||||
} else {
|
||||
continue;
|
||||
@ -621,40 +824,72 @@ macro_rules! match_tokens {
|
||||
}
|
||||
}
|
||||
|
||||
struct LintDeclSearchResult<'a> {
|
||||
token_kind: TokenKind,
|
||||
content: &'a str,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||
fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
|
||||
let mut offset = 0usize;
|
||||
let mut iter = tokenize(contents).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
offset = range.end;
|
||||
(t.kind, &contents[range])
|
||||
|
||||
LintDeclSearchResult {
|
||||
token_kind: t.kind,
|
||||
content: &contents[range.clone()],
|
||||
range,
|
||||
}
|
||||
});
|
||||
|
||||
while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
|
||||
while let Some(LintDeclSearchResult { range, .. }) = iter.find(
|
||||
|LintDeclSearchResult {
|
||||
token_kind, content, ..
|
||||
}| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
|
||||
) {
|
||||
let start = range.start;
|
||||
|
||||
let mut iter = iter
|
||||
.by_ref()
|
||||
.filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
// matches `!{`
|
||||
match_tokens!(iter, Bang OpenBrace);
|
||||
match iter.next() {
|
||||
// #[clippy::version = "version"] pub
|
||||
Some((TokenKind::Pound, _)) => {
|
||||
Some(LintDeclSearchResult {
|
||||
token_kind: TokenKind::Pound,
|
||||
..
|
||||
}) => {
|
||||
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
|
||||
},
|
||||
// pub
|
||||
Some((TokenKind::Ident, _)) => (),
|
||||
Some(LintDeclSearchResult {
|
||||
token_kind: TokenKind::Ident,
|
||||
..
|
||||
}) => (),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
let (name, group, desc) = match_tokens!(
|
||||
iter,
|
||||
// LINT_NAME
|
||||
Ident(name) Comma
|
||||
// group,
|
||||
Ident(group) Comma
|
||||
// "description" }
|
||||
Literal{..}(desc) CloseBrace
|
||||
// "description"
|
||||
Literal{..}(desc)
|
||||
);
|
||||
lints.push(Lint::new(name, group, desc, module));
|
||||
|
||||
if let Some(LintDeclSearchResult {
|
||||
token_kind: TokenKind::CloseBrace,
|
||||
range,
|
||||
..
|
||||
}) = iter.next()
|
||||
{
|
||||
lints.push(Lint::new(name, group, desc, module, start..range.end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -664,12 +899,24 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
|
||||
let mut iter = tokenize(contents).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
offset = range.end;
|
||||
(t.kind, &contents[range])
|
||||
|
||||
LintDeclSearchResult {
|
||||
token_kind: t.kind,
|
||||
content: &contents[range.clone()],
|
||||
range,
|
||||
}
|
||||
});
|
||||
while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
|
||||
let mut iter = iter
|
||||
.by_ref()
|
||||
.filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
|
||||
while let Some(LintDeclSearchResult { range, .. }) = iter.find(
|
||||
|LintDeclSearchResult {
|
||||
token_kind, content, ..
|
||||
}| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
|
||||
) {
|
||||
let start = range.start;
|
||||
|
||||
let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
|
||||
!matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
|
||||
});
|
||||
let (name, reason) = match_tokens!(
|
||||
iter,
|
||||
// !{
|
||||
@ -680,10 +927,16 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
|
||||
Ident Ident(name) Comma
|
||||
// "description"
|
||||
Literal{kind: LiteralKind::Str{..},..}(reason)
|
||||
// }
|
||||
CloseBrace
|
||||
);
|
||||
lints.push(DeprecatedLint::new(name, reason));
|
||||
|
||||
if let Some(LintDeclSearchResult {
|
||||
token_kind: TokenKind::CloseBrace,
|
||||
range,
|
||||
..
|
||||
}) = iter.next()
|
||||
{
|
||||
lints.push(DeprecatedLint::new(name, reason, start..range.end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,8 +946,14 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
|
||||
let mut iter = tokenize(line).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
offset = range.end;
|
||||
(t.kind, &line[range])
|
||||
|
||||
LintDeclSearchResult {
|
||||
token_kind: t.kind,
|
||||
content: &line[range.clone()],
|
||||
range,
|
||||
}
|
||||
});
|
||||
|
||||
let (old_name, new_name) = match_tokens!(
|
||||
iter,
|
||||
// ("old_name",
|
||||
@ -844,10 +1103,25 @@ mod tests {
|
||||
"#;
|
||||
let mut result = Vec::new();
|
||||
parse_contents(CONTENTS, "module_name", &mut result);
|
||||
for r in &mut result {
|
||||
r.declaration_range = Range::default();
|
||||
}
|
||||
|
||||
let expected = vec![
|
||||
Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
|
||||
Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
|
||||
Lint::new(
|
||||
"ptr_arg",
|
||||
"style",
|
||||
"\"really long text\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new(
|
||||
"doc_markdown",
|
||||
"pedantic",
|
||||
"\"single line\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
];
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
@ -865,10 +1139,14 @@ mod tests {
|
||||
|
||||
let mut result = Vec::new();
|
||||
parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
|
||||
for r in &mut result {
|
||||
r.declaration_range = Range::default();
|
||||
}
|
||||
|
||||
let expected = vec![DeprecatedLint::new(
|
||||
"should_assert_eq",
|
||||
"\"`assert!()` will be more flexible with RFC 2011\"",
|
||||
Range::default(),
|
||||
)];
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
@ -876,15 +1154,34 @@ mod tests {
|
||||
#[test]
|
||||
fn test_usable_lints() {
|
||||
let lints = vec![
|
||||
Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"Not Deprecated",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"internal",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"internal_style",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
];
|
||||
let expected = vec![Lint::new(
|
||||
"should_assert_eq2",
|
||||
"Not Deprecated",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
)];
|
||||
assert_eq!(expected, Lint::usable_lints(&lints));
|
||||
}
|
||||
@ -892,21 +1189,33 @@ mod tests {
|
||||
#[test]
|
||||
fn test_by_lint_group() {
|
||||
let lints = vec![
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"group2",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
];
|
||||
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
|
||||
expected.insert(
|
||||
"group1".to_string(),
|
||||
vec![
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
],
|
||||
);
|
||||
expected.insert(
|
||||
"group2".to_string(),
|
||||
vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
|
||||
vec![Lint::new(
|
||||
"should_assert_eq2",
|
||||
"group2",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
)],
|
||||
);
|
||||
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
|
||||
}
|
||||
@ -914,8 +1223,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_gen_deprecated() {
|
||||
let lints = vec![
|
||||
DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""),
|
||||
DeprecatedLint::new("another_deprecated", "\"will be removed\""),
|
||||
DeprecatedLint::new(
|
||||
"should_assert_eq",
|
||||
"\"has been superseded by should_assert_eq2\"",
|
||||
Range::default(),
|
||||
),
|
||||
DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
|
||||
];
|
||||
|
||||
let expected = GENERATED_FILE_COMMENT.to_string()
|
||||
@ -940,9 +1253,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_gen_lint_group_list() {
|
||||
let lints = vec![
|
||||
Lint::new("abc", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
|
||||
Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
|
||||
Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
|
||||
];
|
||||
let expected = GENERATED_FILE_COMMENT.to_string()
|
||||
+ &[
|
||||
|
@ -1,16 +1,21 @@
|
||||
// NOTE: if you add a deprecated lint in this file, please add a corresponding test in
|
||||
// tests/ui/deprecated.rs
|
||||
// NOTE: Entries should be created with `cargo dev deprecate`
|
||||
|
||||
/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
|
||||
/// enables the simple extraction of the metadata without changing the current deprecation
|
||||
/// declaration.
|
||||
pub struct ClippyDeprecatedLint;
|
||||
pub struct ClippyDeprecatedLint {
|
||||
#[allow(dead_code)]
|
||||
pub desc: &'static str,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! declare_deprecated_lint {
|
||||
{ $(#[$attr:meta])* pub $name: ident, $_reason: expr} => {
|
||||
{ $(#[$attr:meta])* pub $name: ident, $reason: literal} => {
|
||||
$(#[$attr])*
|
||||
#[allow(dead_code)]
|
||||
pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {};
|
||||
pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {
|
||||
desc: $reason
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
|
||||
LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
|
||||
LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON),
|
||||
LintId::of(utils::internal_lints::DEFAULT_LINT),
|
||||
LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
|
||||
LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
||||
|
@ -10,6 +10,8 @@ store.register_lints(&[
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::DEFAULT_DEPRECATION_REASON,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::IF_CHAIN_STYLE,
|
||||
|
@ -159,7 +159,7 @@ macro_rules! declare_clippy_lint {
|
||||
}
|
||||
|
||||
#[cfg(feature = "internal")]
|
||||
mod deprecated_lints;
|
||||
pub mod deprecated_lints;
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
mod utils;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
|
||||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
@ -338,6 +339,46 @@ declare_clippy_lint! {
|
||||
"checking if all necessary steps were taken when adding a MSRV to a lint"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for cases of an auto-generated deprecated lint without an updated reason,
|
||||
/// i.e. `"default deprecation note"`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Indicates that the documentation is incomplete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// declare_deprecated_lint! {
|
||||
/// /// ### What it does
|
||||
/// /// Nothing. This lint has been deprecated.
|
||||
/// ///
|
||||
/// /// ### Deprecation reason
|
||||
/// /// TODO
|
||||
/// #[clippy::version = "1.63.0"]
|
||||
/// pub COOL_LINT,
|
||||
/// "default deprecation note"
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// declare_deprecated_lint! {
|
||||
/// /// ### What it does
|
||||
/// /// Nothing. This lint has been deprecated.
|
||||
/// ///
|
||||
/// /// ### Deprecation reason
|
||||
/// /// This lint has been replaced by `cooler_lint`
|
||||
/// #[clippy::version = "1.63.0"]
|
||||
/// pub COOL_LINT,
|
||||
/// "this lint has been replaced by `cooler_lint`"
|
||||
/// }
|
||||
/// ```
|
||||
pub DEFAULT_DEPRECATION_REASON,
|
||||
internal,
|
||||
"found 'default deprecation note' in a deprecated lint declaration"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
||||
impl EarlyLintPass for ClippyLintsInternal {
|
||||
@ -375,42 +416,67 @@ pub struct LintWithoutLintPass {
|
||||
registered_lints: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
|
||||
impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
|
||||
if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
|
||||
|| is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
|
||||
if is_lint_ref_type(cx, ty) {
|
||||
let is_lint_ref_ty = is_lint_ref_type(cx, ty);
|
||||
if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
|
||||
check_invalid_clippy_version_attribute(cx, item);
|
||||
|
||||
let expr = &cx.tcx.hir().body(body_id).value;
|
||||
if_chain! {
|
||||
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
|
||||
if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
|
||||
let field = fields
|
||||
.iter()
|
||||
.find(|f| f.ident.as_str() == "desc")
|
||||
.expect("lints must have a description field");
|
||||
if let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Str(ref sym, _),
|
||||
..
|
||||
}) = field.expr.kind;
|
||||
if sym.as_str() == "default lint description";
|
||||
let fields;
|
||||
if is_lint_ref_ty {
|
||||
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
|
||||
&& let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
|
||||
fields = struct_fields;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
|
||||
fields = struct_fields;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
then {
|
||||
let field = fields
|
||||
.iter()
|
||||
.find(|f| f.ident.as_str() == "desc")
|
||||
.expect("lints must have a description field");
|
||||
|
||||
if let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Str(ref sym, _),
|
||||
..
|
||||
}) = field.expr.kind
|
||||
{
|
||||
let sym_str = sym.as_str();
|
||||
if is_lint_ref_ty {
|
||||
if sym_str == "default lint description" {
|
||||
span_lint(
|
||||
cx,
|
||||
DEFAULT_LINT,
|
||||
item.span,
|
||||
&format!("the lint `{}` has the default lint description", item.ident.name),
|
||||
);
|
||||
}
|
||||
|
||||
self.declared_lints.insert(item.ident.name, item.span);
|
||||
} else if sym_str == "default deprecation note" {
|
||||
span_lint(
|
||||
cx,
|
||||
DEFAULT_LINT,
|
||||
DEFAULT_DEPRECATION_REASON,
|
||||
item.span,
|
||||
&format!("the lint `{}` has the default lint description", item.ident.name),
|
||||
&format!("the lint `{}` has the default deprecation reason", item.ident.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.declared_lints.insert(item.ident.name, item.span);
|
||||
}
|
||||
} else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
|
||||
if !matches!(
|
||||
|
@ -847,7 +847,7 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
|
||||
.find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
|
||||
}
|
||||
|
||||
fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let hir::TyKind::Path(ref path) = ty.kind {
|
||||
if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
|
||||
return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
|
||||
|
@ -24,6 +24,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
|
||||
/// All crates used in UI tests are listed here
|
||||
static TEST_DEPENDENCIES: &[&str] = &[
|
||||
"clap",
|
||||
"clippy_lints",
|
||||
"clippy_utils",
|
||||
"derive_new",
|
||||
"futures",
|
||||
@ -44,6 +45,8 @@ static TEST_DEPENDENCIES: &[&str] = &[
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate clap;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate clippy_lints;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate clippy_utils;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate derive_new;
|
||||
|
30
tests/ui-internal/default_deprecation_reason.rs
Normal file
30
tests/ui-internal/default_deprecation_reason.rs
Normal file
@ -0,0 +1,30 @@
|
||||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate clippy_lints;
|
||||
use clippy_lints::deprecated_lints::ClippyDeprecatedLint;
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// ### What it does
|
||||
/// Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// ### Deprecation reason
|
||||
/// TODO
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub COOL_LINT_DEFAULT,
|
||||
"default deprecation note"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// ### What it does
|
||||
/// Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// ### Deprecation reason
|
||||
/// This lint has been replaced by `cooler_lint`
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub COOL_LINT,
|
||||
"this lint has been replaced by `cooler_lint`"
|
||||
}
|
||||
|
||||
fn main() {}
|
22
tests/ui-internal/default_deprecation_reason.stderr
Normal file
22
tests/ui-internal/default_deprecation_reason.stderr
Normal file
@ -0,0 +1,22 @@
|
||||
error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason
|
||||
--> $DIR/default_deprecation_reason.rs:8:1
|
||||
|
|
||||
LL | / declare_deprecated_lint! {
|
||||
LL | | /// ### What it does
|
||||
LL | | /// Nothing. This lint has been deprecated.
|
||||
LL | | ///
|
||||
... |
|
||||
LL | | "default deprecation note"
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/default_deprecation_reason.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::internal)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]`
|
||||
= note: this error originates in the macro `declare_deprecated_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue
Block a user