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:
bors 2022-06-27 00:32:22 +00:00
commit 57e7e1d7d8
13 changed files with 529 additions and 69 deletions

View File

@ -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"

View File

@ -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()
}

View File

@ -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()

View File

@ -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()
+ &[

View File

@ -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
};
}
}

View File

@ -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),

View File

@ -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,

View File

@ -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;

View File

@ -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!(

View File

@ -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);

View File

@ -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;

View 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() {}

View 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