mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Merge commit 'e645f93552c3926a0bb481a777df120b7bce986f'
This commit is contained in:
@@ -20,7 +20,8 @@ jobs:
|
||||
- name: Check Changelog
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then
|
||||
# Checks that the PR body contains a changelog entry, ignoring the placeholder from the PR template.
|
||||
if [[ -z $(grep -oP 'changelog: *\K(?!\[`lint_name`\])\S+' <<< "$PR_BODY") ]]; then
|
||||
echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.95"
|
||||
version = "0.1.96"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -51,7 +51,7 @@ this group to help with triaging, which can include:
|
||||
busy or wants to abandon it. If the reviewer is busy, the PR can be
|
||||
reassigned to someone else.
|
||||
|
||||
Checkout: https://triage.rust-lang.org/triage/rust-lang/rust-clippy to
|
||||
Checkout: <https://triage.rust-lang.org/triage/rust-lang/rust-clippy> to
|
||||
monitor PRs.
|
||||
|
||||
While not part of their duties, contributors are encouraged to review PRs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.95"
|
||||
version = "0.1.96"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::parse::cursor::{self, Capture, Cursor};
|
||||
use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists,
|
||||
expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target,
|
||||
@@ -40,7 +39,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name
|
||||
};
|
||||
|
||||
remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default());
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
println!("info: `{name}` has successfully been deprecated");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
@@ -74,7 +73,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam
|
||||
updater.update_file(e.path(), &mut update_fn);
|
||||
}
|
||||
}
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
println!("info: `{old_name}` has successfully been uplifted as `{new_name}`");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
@@ -151,7 +150,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam
|
||||
updater.update_file(e.path(), &mut update_fn);
|
||||
}
|
||||
}
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
|
||||
println!("Renamed `{old_name}` to `{new_name}`");
|
||||
println!("All code referencing the old name has been updated");
|
||||
@@ -172,11 +171,11 @@ fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_
|
||||
delete_file_if_exists(lint.path.as_ref())
|
||||
} else {
|
||||
updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus {
|
||||
let mut start = &src[..lint.declaration_range.start];
|
||||
let mut start = &src[..lint.declaration_range.start as usize];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
let mut end = &src[lint.declaration_range.end..];
|
||||
let mut end = &src[lint.declaration_range.end as usize..];
|
||||
if end.starts_with("\n\n") {
|
||||
end = &end[1..];
|
||||
}
|
||||
|
||||
+29
-1
@@ -1,3 +1,6 @@
|
||||
use crate::generate::gen_sorted_lints_file;
|
||||
use crate::new_parse_cx;
|
||||
use crate::parse::VecBuf;
|
||||
use crate::utils::{
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads,
|
||||
walk_dir_no_dot_or_target,
|
||||
@@ -326,10 +329,35 @@ fn run_rustfmt(update_mode: UpdateMode) {
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
pub fn run(update_mode: UpdateMode) {
|
||||
run_rustfmt(update_mode);
|
||||
fmt_syms(update_mode);
|
||||
if let Err(e) = fmt_conf(update_mode.is_check()) {
|
||||
e.display();
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
new_parse_cx(|cx| {
|
||||
let mut data = cx.parse_lint_decls();
|
||||
let (mut lints, passes) = data.split_by_lint_file();
|
||||
let mut updater = FileUpdater::default();
|
||||
let mut ranges = VecBuf::with_capacity(256);
|
||||
|
||||
for passes in passes {
|
||||
let path = passes[0].path.clone();
|
||||
let mut lints = lints.remove(&*path);
|
||||
let lints = lints.as_deref_mut().unwrap_or_default();
|
||||
updater.update_file_checked("cargo dev fmt", update_mode, &path, &mut |_, src, dst| {
|
||||
gen_sorted_lints_file(src, dst, lints, passes, &mut ranges);
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
});
|
||||
}
|
||||
|
||||
for (&path, lints) in &mut lints {
|
||||
updater.update_file_checked("cargo dev fmt", update_mode, path, &mut |_, src, dst| {
|
||||
gen_sorted_lints_file(src, dst, lints, &mut [], &mut ranges);
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
run_rustfmt(update_mode);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
use crate::parse::cursor::Cursor;
|
||||
use crate::parse::{Lint, LintData, LintPass, VecBuf};
|
||||
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
|
||||
use core::range::Range;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::path::{self, Path};
|
||||
|
||||
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";
|
||||
|
||||
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
||||
|
||||
impl LintData<'_> {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn gen_decls(&self, update_mode: UpdateMode) {
|
||||
let mut updater = FileUpdater::default();
|
||||
|
||||
let mut lints: Vec<_> = self.lints.iter().map(|(&x, y)| (x, y)).collect();
|
||||
lints.sort_by_key(|&(x, _)| x);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"CHANGELOG.md",
|
||||
&mut update_text_region_fn(
|
||||
"<!-- begin autogenerated links to lint list -->\n",
|
||||
"<!-- end autogenerated links to lint list -->",
|
||||
|dst| {
|
||||
for &(lint, _) in &lints {
|
||||
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let mut active = Vec::with_capacity(lints.len());
|
||||
let mut deprecated = Vec::with_capacity(lints.len() / 8);
|
||||
let mut renamed = Vec::with_capacity(lints.len() / 8);
|
||||
for &(name, lint) in &lints {
|
||||
match lint {
|
||||
Lint::Active(lint) => active.push((name, lint)),
|
||||
Lint::Deprecated(lint) => deprecated.push((name, lint)),
|
||||
Lint::Renamed(lint) => renamed.push((name, lint)),
|
||||
}
|
||||
}
|
||||
active.sort_by_key(|&(_, lint)| lint.module);
|
||||
|
||||
// Round to avoid updating the readme every time a lint is added/deprecated.
|
||||
let lint_count = active.len() / 50 * 50;
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"book/src/README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"clippy_lints/src/deprecated_lints.rs",
|
||||
&mut |_, src, dst| {
|
||||
let mut cursor = Cursor::new(src);
|
||||
assert!(
|
||||
cursor.find_ident("declare_with_version").is_some()
|
||||
&& cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
dst.push_str(&src[..cursor.pos() as usize]);
|
||||
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
|
||||
for &(name, data) in &deprecated {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.reason,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str(
|
||||
"]}\n\n\
|
||||
#[rustfmt::skip]\n\
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
|
||||
",
|
||||
);
|
||||
for &(name, data) in &renamed {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.new_name,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str("]}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/deprecated.rs",
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
for &(lint, _) in &deprecated {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/rename.rs",
|
||||
&mut move |_, src, dst| {
|
||||
let mut seen_lints = HashSet::new();
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for &(_, lint) in &renamed {
|
||||
if seen_lints.insert(lint.new_name) {
|
||||
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
for &(lint, _) in &renamed {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| {
|
||||
let Some(path::Component::Normal(name)) = lint.path.components().next() else {
|
||||
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
|
||||
panic!(
|
||||
"internal error: can't read crate name from path `{}`",
|
||||
lint.path.display()
|
||||
);
|
||||
};
|
||||
name
|
||||
}) {
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/lib.rs"),
|
||||
&mut update_text_region_fn(
|
||||
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
|
||||
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|
||||
|dst| {
|
||||
let mut prev = "";
|
||||
for &(_, lint) in &lints {
|
||||
if lint.module != prev {
|
||||
writeln!(dst, "mod {};", lint.module).unwrap();
|
||||
prev = lint.module;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/declared_lints.rs"),
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
|
||||
let mut buf = String::new();
|
||||
for &(name, lint) in &lints {
|
||||
buf.clear();
|
||||
buf.push_str(name);
|
||||
buf.make_ascii_uppercase();
|
||||
if lint.module.is_empty() {
|
||||
writeln!(dst, " crate::{buf}_INFO,").unwrap();
|
||||
} else {
|
||||
writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap();
|
||||
}
|
||||
}
|
||||
dst.push_str("];\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LintPass<'_> {
|
||||
pub fn gen_mac(&self, dst: &mut String) {
|
||||
let mut line_start = dst.len();
|
||||
dst.extend([self.mac.name(), "!("]);
|
||||
let has_docs = write_comment_lines(self.docs, "\n ", dst);
|
||||
let (list_indent, list_multi_end, end) = if has_docs {
|
||||
dst.push_str("\n ");
|
||||
line_start = dst.len() - 4;
|
||||
(" ", "\n ", "]\n);")
|
||||
} else {
|
||||
(" ", "\n", "]);")
|
||||
};
|
||||
dst.push_str(self.name);
|
||||
if let Some(lt) = self.lt {
|
||||
dst.extend(["<", lt, ">"]);
|
||||
}
|
||||
dst.push_str(" => [");
|
||||
let fmt = write_list(
|
||||
self.lints.iter().copied(),
|
||||
80usize.saturating_sub(dst.len() - line_start),
|
||||
list_indent,
|
||||
dst,
|
||||
);
|
||||
if matches!(fmt, ListFmt::MultiLine) {
|
||||
dst.push_str(list_multi_end);
|
||||
}
|
||||
dst.push_str(end);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_comment_lines(s: &str, prefix: &str, dst: &mut String) -> bool {
|
||||
let mut has_doc = false;
|
||||
for line in s.split('\n') {
|
||||
let line = line.trim_start();
|
||||
if !line.is_empty() {
|
||||
has_doc = true;
|
||||
dst.extend([prefix, line]);
|
||||
}
|
||||
}
|
||||
has_doc
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ListFmt {
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
}
|
||||
|
||||
fn write_list<'a>(
|
||||
items: impl Iterator<Item = &'a str> + Clone,
|
||||
single_line_limit: usize,
|
||||
indent: &str,
|
||||
dst: &mut String,
|
||||
) -> ListFmt {
|
||||
let len = items.clone().map(str::len).sum::<usize>();
|
||||
if len > single_line_limit {
|
||||
for item in items {
|
||||
dst.extend(["\n", indent, item, ","]);
|
||||
}
|
||||
ListFmt::MultiLine
|
||||
} else {
|
||||
let _ = write!(dst, "{}", items.format(", "));
|
||||
ListFmt::SingleLine
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the contents of a lint's source file with all the lint and lint pass
|
||||
/// declarations sorted.
|
||||
pub fn gen_sorted_lints_file(
|
||||
src: &str,
|
||||
dst: &mut String,
|
||||
lints: &mut [(&str, Range<u32>)],
|
||||
passes: &mut [LintPass<'_>],
|
||||
ranges: &mut VecBuf<Range<u32>>,
|
||||
) {
|
||||
ranges.with(|ranges| {
|
||||
ranges.extend(lints.iter().map(|&(_, x)| x));
|
||||
ranges.extend(passes.iter().map(|x| x.decl_range));
|
||||
ranges.sort_unstable_by_key(|x| x.start);
|
||||
|
||||
lints.sort_unstable_by_key(|&(x, _)| x);
|
||||
passes.sort_by_key(|x| x.name);
|
||||
|
||||
let mut ranges = ranges.iter();
|
||||
let pos = if let Some(range) = ranges.next() {
|
||||
dst.push_str(&src[..range.start as usize]);
|
||||
for &(_, range) in &*lints {
|
||||
dst.push_str(&src[range.start as usize..range.end as usize]);
|
||||
dst.push_str("\n\n");
|
||||
}
|
||||
for pass in passes {
|
||||
pass.gen_mac(dst);
|
||||
dst.push_str("\n\n");
|
||||
}
|
||||
range.end
|
||||
} else {
|
||||
dst.push_str(src);
|
||||
return;
|
||||
};
|
||||
|
||||
let pos = ranges.fold(pos, |start, range| {
|
||||
let s = &src[start as usize..range.start as usize];
|
||||
dst.push_str(if s.trim_start().is_empty() {
|
||||
// Only whitespace between this and the previous item. No need to keep that.
|
||||
""
|
||||
} else if src[..pos as usize].ends_with("\n\n")
|
||||
&& let Some(s) = s.strip_prefix("\n\n")
|
||||
{
|
||||
// Empty line before and after. Remove one of them.
|
||||
s
|
||||
} else {
|
||||
// Remove only full lines unless something is in the way.
|
||||
s.strip_prefix('\n').unwrap_or(s)
|
||||
});
|
||||
range.end
|
||||
});
|
||||
|
||||
// Since we always generate an empty line at the end, make sure to always skip it.
|
||||
let s = &src[pos as usize..];
|
||||
dst.push_str(s.strip_prefix('\n').map_or(s, |s| s.strip_prefix('\n').unwrap_or(s)));
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#![feature(
|
||||
exit_status_error,
|
||||
if_let_guard,
|
||||
new_range,
|
||||
new_range_api,
|
||||
os_str_slice,
|
||||
@@ -33,8 +32,8 @@
|
||||
pub mod serve;
|
||||
pub mod setup;
|
||||
pub mod sync;
|
||||
pub mod update_lints;
|
||||
|
||||
mod generate;
|
||||
mod parse;
|
||||
mod utils;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use clippy_dev::{
|
||||
ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync,
|
||||
update_lints,
|
||||
};
|
||||
use std::env;
|
||||
|
||||
@@ -27,7 +26,9 @@ fn main() {
|
||||
allow_no_vcs,
|
||||
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
|
||||
DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)),
|
||||
DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))),
|
||||
DevCommand::UpdateLints { check } => {
|
||||
new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::from_check(check)));
|
||||
},
|
||||
DevCommand::NewLint {
|
||||
pass,
|
||||
name,
|
||||
@@ -35,7 +36,7 @@ fn main() {
|
||||
r#type,
|
||||
msrv,
|
||||
} => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) {
|
||||
Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)),
|
||||
Ok(()) => new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::Change)),
|
||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||
},
|
||||
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||
|
||||
+166
-21
@@ -1,7 +1,7 @@
|
||||
pub mod cursor;
|
||||
|
||||
use self::cursor::{Capture, Cursor};
|
||||
use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target};
|
||||
use crate::utils::{ErrAction, File, Scoped, expect_action, slice_groups_mut, walk_dir_no_dot_or_target};
|
||||
use core::fmt::{self, Display, Write as _};
|
||||
use core::range::Range;
|
||||
use rustc_arena::DroplessArena;
|
||||
@@ -13,6 +13,7 @@
|
||||
pub struct ParseCxImpl<'cx> {
|
||||
pub arena: &'cx DroplessArena,
|
||||
pub str_buf: StrBuf,
|
||||
pub str_list_buf: VecBuf<&'cx str>,
|
||||
}
|
||||
pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>;
|
||||
|
||||
@@ -22,6 +23,7 @@ pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env,
|
||||
f(&mut Scoped::new(ParseCxImpl {
|
||||
arena: &arena,
|
||||
str_buf: StrBuf::with_capacity(128),
|
||||
str_list_buf: VecBuf::with_capacity(128),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -82,6 +84,20 @@ pub fn with<T>(&mut self, f: impl FnOnce(&mut String) -> T) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecBuf<T>(Vec<T>);
|
||||
impl<T> VecBuf<T> {
|
||||
/// Creates a new buffer with the specified initial capacity.
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self(Vec::with_capacity(cap))
|
||||
}
|
||||
|
||||
/// Performs an operation with the freshly cleared buffer.
|
||||
pub fn with<R>(&mut self, f: impl FnOnce(&mut Vec<T>) -> R) -> R {
|
||||
self.0.clear();
|
||||
f(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum LintTool {
|
||||
Rustc,
|
||||
@@ -128,7 +144,7 @@ pub struct ActiveLint<'cx> {
|
||||
pub group: &'cx str,
|
||||
pub module: &'cx str,
|
||||
pub path: PathBuf,
|
||||
pub declaration_range: Range<usize>,
|
||||
pub declaration_range: Range<u32>,
|
||||
}
|
||||
|
||||
pub struct DeprecatedLint<'cx> {
|
||||
@@ -147,8 +163,59 @@ pub enum Lint<'cx> {
|
||||
Renamed(RenamedLint<'cx>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LintPassMac {
|
||||
Declare,
|
||||
Impl,
|
||||
}
|
||||
impl LintPassMac {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Declare => "declare_lint_pass",
|
||||
Self::Impl => "impl_lint_pass",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LintPass<'cx> {
|
||||
/// The raw text of the documentation comments. May include leading/trailing
|
||||
/// whitespace and empty lines.
|
||||
pub docs: &'cx str,
|
||||
pub name: &'cx str,
|
||||
pub lt: Option<&'cx str>,
|
||||
pub mac: LintPassMac,
|
||||
pub decl_range: Range<u32>,
|
||||
pub lints: &'cx [&'cx str],
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
pub struct LintData<'cx> {
|
||||
pub lints: FxHashMap<&'cx str, Lint<'cx>>,
|
||||
pub lint_passes: Vec<LintPass<'cx>>,
|
||||
}
|
||||
impl<'cx> LintData<'cx> {
|
||||
#[expect(clippy::type_complexity)]
|
||||
pub fn split_by_lint_file<'s>(
|
||||
&'s mut self,
|
||||
) -> (
|
||||
FxHashMap<&'s Path, Vec<(&'s str, Range<u32>)>>,
|
||||
impl Iterator<Item = &'s mut [LintPass<'cx>]>,
|
||||
) {
|
||||
#[expect(clippy::default_trait_access)]
|
||||
let mut lints = FxHashMap::with_capacity_and_hasher(500, Default::default());
|
||||
for (&name, lint) in &self.lints {
|
||||
if let Lint::Active(lint) = lint {
|
||||
lints
|
||||
.entry(&*lint.path)
|
||||
.or_insert_with(|| Vec::with_capacity(8))
|
||||
.push((name, lint.declaration_range));
|
||||
}
|
||||
}
|
||||
let passes = slice_groups_mut(&mut self.lint_passes, |head, tail| {
|
||||
tail.iter().take_while(|&x| x.path == head.path).count()
|
||||
});
|
||||
(lints, passes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx> ParseCxImpl<'cx> {
|
||||
@@ -158,6 +225,7 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
let mut data = LintData {
|
||||
#[expect(clippy::default_trait_access)]
|
||||
lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()),
|
||||
lint_passes: Vec::with_capacity(400),
|
||||
};
|
||||
|
||||
let mut contents = String::new();
|
||||
@@ -193,7 +261,7 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
self.str_buf
|
||||
.alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::")
|
||||
};
|
||||
self.parse_clippy_lint_decls(
|
||||
self.parse_lint_src_file(
|
||||
e.path(),
|
||||
File::open_read_to_cleared_string(e.path(), &mut contents),
|
||||
module,
|
||||
@@ -208,11 +276,11 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
}
|
||||
|
||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||
fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) {
|
||||
fn parse_lint_src_file(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use cursor::Pat::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
static LINT_DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !{ /// docs
|
||||
Bang, OpenBrace, AnyComment,
|
||||
// #[clippy::version = "version"]
|
||||
@@ -220,24 +288,101 @@ fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx
|
||||
// pub NAME, GROUP,
|
||||
Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static PASS_DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !( NAME <'lt> => [
|
||||
Bang, OpenParen, CaptureDocLines, CaptureIdent, CaptureOptLifetimeArg, FatArrow, OpenBracket,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(contents);
|
||||
let mut captures = [Capture::EMPTY; 2];
|
||||
while let Some(start) = cursor.find_ident("declare_clippy_lint") {
|
||||
if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) {
|
||||
assert!(
|
||||
data.lints
|
||||
.insert(
|
||||
self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])),
|
||||
Lint::Active(ActiveLint {
|
||||
group: self.arena.alloc_str(cursor.get_text(captures[1])),
|
||||
module,
|
||||
path: path.into(),
|
||||
declaration_range: start as usize..cursor.pos() as usize,
|
||||
}),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
let mut captures = [Capture::EMPTY; 3];
|
||||
while let Some(mac_name) = cursor.find_any_ident() {
|
||||
match cursor.get_text(mac_name) {
|
||||
"declare_clippy_lint"
|
||||
if cursor.match_all(LINT_DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) =>
|
||||
{
|
||||
assert!(
|
||||
data.lints
|
||||
.insert(
|
||||
self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])),
|
||||
Lint::Active(ActiveLint {
|
||||
group: self.arena.alloc_str(cursor.get_text(captures[1])),
|
||||
module,
|
||||
path: path.into(),
|
||||
declaration_range: mac_name.pos..cursor.pos(),
|
||||
}),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
},
|
||||
mac @ ("declare_lint_pass" | "impl_lint_pass") if cursor.match_all(PASS_DECL_TOKENS, &mut captures) => {
|
||||
let mac = if matches!(mac, "declare_lint_pass") {
|
||||
LintPassMac::Declare
|
||||
} else {
|
||||
LintPassMac::Impl
|
||||
};
|
||||
let docs = match cursor.get_text(captures[0]) {
|
||||
"" => "",
|
||||
x => self.arena.alloc_str(x),
|
||||
};
|
||||
let name = self.arena.alloc_str(cursor.get_text(captures[1]));
|
||||
let lt = cursor.get_text(captures[2]);
|
||||
let lt = if lt.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.arena.alloc_str(lt))
|
||||
};
|
||||
|
||||
let lints = self.str_list_buf.with(|buf| {
|
||||
// Parses a comma separated list of paths and converts each path
|
||||
// to a string with whitespace removed.
|
||||
while !cursor.match_pat(CloseBracket) {
|
||||
buf.push(self.str_buf.with(|buf| {
|
||||
if cursor.match_pat(DoubleColon) {
|
||||
buf.push_str("::");
|
||||
}
|
||||
let capture = cursor.capture_ident()?;
|
||||
buf.push_str(cursor.get_text(capture));
|
||||
while cursor.match_pat(DoubleColon) {
|
||||
buf.push_str("::");
|
||||
let capture = cursor.capture_ident()?;
|
||||
buf.push_str(cursor.get_text(capture));
|
||||
}
|
||||
Some(self.arena.alloc_str(buf))
|
||||
})?);
|
||||
|
||||
if !cursor.match_pat(Comma) {
|
||||
if !cursor.match_pat(CloseBracket) {
|
||||
return None;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The arena panics when allocating a size of zero.
|
||||
Some(if buf.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
buf.sort_unstable();
|
||||
&*self.arena.alloc_slice(buf)
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(lints) = lints
|
||||
&& cursor.match_all(&[CloseParen, Semi], &mut [])
|
||||
{
|
||||
data.lint_passes.push(LintPass {
|
||||
docs,
|
||||
name,
|
||||
lt,
|
||||
mac,
|
||||
decl_range: mac_name.pos..cursor.pos(),
|
||||
lints,
|
||||
path: path.into(),
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub enum Pat<'a> {
|
||||
/// Matches any number of comments and doc comments.
|
||||
AnyComment,
|
||||
Ident(&'a str),
|
||||
CaptureDocLines,
|
||||
CaptureIdent,
|
||||
LitStr,
|
||||
CaptureLitStr,
|
||||
@@ -22,12 +23,14 @@ pub enum Pat<'a> {
|
||||
Comma,
|
||||
DoubleColon,
|
||||
Eq,
|
||||
FatArrow,
|
||||
Lifetime,
|
||||
Lt,
|
||||
Gt,
|
||||
OpenBrace,
|
||||
OpenBracket,
|
||||
OpenParen,
|
||||
CaptureOptLifetimeArg,
|
||||
Pound,
|
||||
Semi,
|
||||
}
|
||||
@@ -112,6 +115,7 @@ pub fn step(&mut self) {
|
||||
///
|
||||
/// For each capture made by the pattern one item will be taken from the capture
|
||||
/// sequence with the result placed inside.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool {
|
||||
loop {
|
||||
match (pat, self.next_token.kind) {
|
||||
@@ -121,7 +125,6 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
Pat::AnyComment,
|
||||
TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. },
|
||||
) => self.step(),
|
||||
(Pat::AnyComment, _) => return true,
|
||||
(Pat::Bang, TokenKind::Bang)
|
||||
| (Pat::CloseBrace, TokenKind::CloseBrace)
|
||||
| (Pat::CloseBracket, TokenKind::CloseBracket)
|
||||
@@ -152,12 +155,48 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
},
|
||||
(Pat::DoubleColon, TokenKind::Colon) => {
|
||||
self.step();
|
||||
if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) {
|
||||
if matches!(self.next_token.kind, TokenKind::Colon) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(Pat::FatArrow, TokenKind::Eq) => {
|
||||
self.step();
|
||||
if matches!(self.next_token.kind, TokenKind::Gt) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(Pat::CaptureOptLifetimeArg, TokenKind::Lt) => {
|
||||
self.step();
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Lifetime { .. } => break,
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
*captures.next().unwrap() = Capture {
|
||||
pos: self.pos,
|
||||
len: self.next_token.len,
|
||||
};
|
||||
self.step();
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Gt => break,
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureOptLifetimeArg, _) => {
|
||||
*captures.next().unwrap() = Capture { pos: 0, len: 0 };
|
||||
return true;
|
||||
},
|
||||
#[rustfmt::skip]
|
||||
(
|
||||
Pat::CaptureLitStr,
|
||||
@@ -173,6 +212,28 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureDocLines, TokenKind::LineComment { doc_style: Some(_) }) => {
|
||||
let pos = self.pos;
|
||||
loop {
|
||||
self.step();
|
||||
if !matches!(
|
||||
self.next_token.kind,
|
||||
TokenKind::Whitespace | TokenKind::LineComment { doc_style: Some(_) }
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*captures.next().unwrap() = Capture {
|
||||
pos,
|
||||
len: self.pos - pos,
|
||||
};
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureDocLines, _) => {
|
||||
*captures.next().unwrap() = Capture::EMPTY;
|
||||
return true;
|
||||
},
|
||||
(Pat::AnyComment, _) => return true,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
@@ -219,8 +280,8 @@ pub fn find_any_ident(&mut self) -> Option<Capture> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume the returns the position of the next non-whitespace token if it's an
|
||||
/// identifier. Returns `None` otherwise.
|
||||
/// Consume the returns the position of the next non-whitespace token if it's the
|
||||
/// specified identifier. Returns `None` otherwise.
|
||||
pub fn match_ident(&mut self, s: &str) -> Option<u32> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
@@ -235,6 +296,23 @@ pub fn match_ident(&mut self, s: &str) -> Option<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes and captures the next non-whitespace token if it's an identifier. Returns
|
||||
/// `None` otherwise.
|
||||
pub fn capture_ident(&mut self) -> Option<Capture> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Ident => {
|
||||
let pos = self.pos;
|
||||
let len = self.next_token.len;
|
||||
self.step();
|
||||
return Some(Capture { pos, len });
|
||||
},
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Continually attempt to match the pattern on subsequent tokens until a match is
|
||||
/// found. Returns whether the pattern was successfully matched.
|
||||
///
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
use crate::parse::cursor::Cursor;
|
||||
use crate::parse::{Lint, LintData, ParseCx};
|
||||
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::path::{self, Path};
|
||||
|
||||
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";
|
||||
|
||||
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
||||
|
||||
/// Runs the `update_lints` command.
|
||||
///
|
||||
/// This updates various generated values from the lint source code.
|
||||
///
|
||||
/// `update_mode` indicates if the files should be updated or if updates should be checked for.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a file path could not read from or then written to
|
||||
pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) {
|
||||
let data = cx.parse_lint_decls();
|
||||
generate_lint_files(update_mode, &data);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) {
|
||||
let mut updater = FileUpdater::default();
|
||||
|
||||
let mut lints: Vec<_> = data.lints.iter().map(|(&x, y)| (x, y)).collect();
|
||||
lints.sort_by_key(|&(x, _)| x);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"CHANGELOG.md",
|
||||
&mut update_text_region_fn(
|
||||
"<!-- begin autogenerated links to lint list -->\n",
|
||||
"<!-- end autogenerated links to lint list -->",
|
||||
|dst| {
|
||||
for &(lint, _) in &lints {
|
||||
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let mut active = Vec::with_capacity(lints.len());
|
||||
let mut deprecated = Vec::with_capacity(lints.len() / 8);
|
||||
let mut renamed = Vec::with_capacity(lints.len() / 8);
|
||||
for &(name, lint) in &lints {
|
||||
match lint {
|
||||
Lint::Active(lint) => active.push((name, lint)),
|
||||
Lint::Deprecated(lint) => deprecated.push((name, lint)),
|
||||
Lint::Renamed(lint) => renamed.push((name, lint)),
|
||||
}
|
||||
}
|
||||
active.sort_by_key(|&(_, lint)| lint.module);
|
||||
|
||||
// Round to avoid updating the readme every time a lint is added/deprecated.
|
||||
let lint_count = active.len() / 50 * 50;
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"book/src/README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"clippy_lints/src/deprecated_lints.rs",
|
||||
&mut |_, src, dst| {
|
||||
let mut cursor = Cursor::new(src);
|
||||
assert!(
|
||||
cursor.find_ident("declare_with_version").is_some()
|
||||
&& cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
dst.push_str(&src[..cursor.pos() as usize]);
|
||||
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
|
||||
for &(name, data) in &deprecated {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.reason,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str(
|
||||
"]}\n\n\
|
||||
#[rustfmt::skip]\n\
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
|
||||
",
|
||||
);
|
||||
for &(name, data) in &renamed {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.new_name,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str("]}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/deprecated.rs",
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
for &(lint, _) in &deprecated {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/rename.rs",
|
||||
&mut move |_, src, dst| {
|
||||
let mut seen_lints = HashSet::new();
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for &(_, lint) in &renamed {
|
||||
if seen_lints.insert(lint.new_name) {
|
||||
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
for &(lint, _) in &renamed {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| {
|
||||
let Some(path::Component::Normal(name)) = lint.path.components().next() else {
|
||||
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
|
||||
panic!(
|
||||
"internal error: can't read crate name from path `{}`",
|
||||
lint.path.display()
|
||||
);
|
||||
};
|
||||
name
|
||||
}) {
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/lib.rs"),
|
||||
&mut update_text_region_fn(
|
||||
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
|
||||
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|
||||
|dst| {
|
||||
let mut prev = "";
|
||||
for &(_, lint) in &lints {
|
||||
if lint.module != prev {
|
||||
writeln!(dst, "mod {};", lint.module).unwrap();
|
||||
prev = lint.module;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/declared_lints.rs"),
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
|
||||
let mut buf = String::new();
|
||||
for &(name, lint) in &lints {
|
||||
buf.clear();
|
||||
buf.push_str(name);
|
||||
buf.make_ascii_uppercase();
|
||||
if lint.module.is_empty() {
|
||||
writeln!(dst, " crate::{buf}_INFO,").unwrap();
|
||||
} else {
|
||||
writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap();
|
||||
}
|
||||
}
|
||||
dst.push_str("];\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::fmt::{self, Display};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::num::NonZero;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::range::Range;
|
||||
@@ -600,3 +601,29 @@ pub fn walk_dir_no_dot_or_target(p: impl AsRef<Path>) -> impl Iterator<Item = ::
|
||||
.is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.'))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn slice_groups_mut<T>(
|
||||
slice: &mut [T],
|
||||
split_idx: impl FnMut(&T, &[T]) -> usize,
|
||||
) -> impl Iterator<Item = &mut [T]> {
|
||||
struct I<'a, T, F> {
|
||||
slice: &'a mut [T],
|
||||
split_idx: F,
|
||||
}
|
||||
impl<'a, T, F: FnMut(&T, &[T]) -> usize> Iterator for I<'a, T, F> {
|
||||
type Item = &'a mut [T];
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (head, tail) = self.slice.split_first()?;
|
||||
let idx = (self.split_idx)(head, tail) + 1;
|
||||
// `mem::take` makes it so `self.slice` isn't reborrowed.
|
||||
if let Some((head, tail)) = mem::take(&mut self.slice).split_at_mut_checked(idx) {
|
||||
self.slice = tail;
|
||||
Some(head)
|
||||
} else {
|
||||
self.slice = &mut [];
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
I { slice, split_idx }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.95"
|
||||
version = "0.1.96"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
restriction,
|
||||
"checks for usage of an item without a `use` statement"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
|
||||
|
||||
pub struct AbsolutePaths {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
suspicious,
|
||||
"almost complete range"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]);
|
||||
|
||||
pub struct AlmostCompleteRange {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"the approximate of a known float constant (in `std::fXX::consts`)"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
|
||||
|
||||
// Tuples are of the form (constant, name, min_digits, msrv)
|
||||
const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
|
||||
(f64::E, "E", 4, None),
|
||||
@@ -111,8 +113,6 @@ fn check_known_consts(&self, cx: &LateContext<'_>, span: Span, s: symbol::Symbol
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
|
||||
|
||||
fn count_digits_after_dot(input: &str) -> usize {
|
||||
input
|
||||
.char_indices()
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
suspicious,
|
||||
"using `Arc` with a type that does not implement `Send` and `Sync`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
||||
|
||||
@@ -57,54 +57,6 @@ fn check_asm_syntax(
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of Intel x86 assembly syntax.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To enforce consistent use of AT&T x86 assembly syntax.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
restriction,
|
||||
"prefer AT&T x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86IntelSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of AT&T x86 assembly syntax.
|
||||
@@ -137,8 +89,56 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
"prefer Intel x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of Intel x86 assembly syntax.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To enforce consistent use of AT&T x86 assembly syntax.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
restriction,
|
||||
"prefer AT&T x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
|
||||
|
||||
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86IntelSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86AttSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
|
||||
|
||||
pub struct AssertionsOnConstants {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
"assigning the result of cloning may be inefficient"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
pub struct AssigningClones {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -63,8 +65,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = e.kind
|
||||
|
||||
+310
-313
@@ -25,106 +25,60 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INLINE_ALWAYS,
|
||||
pedantic,
|
||||
"use of `#[inline(always)]`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint permits lint attributes for lints emitted on the items themself.
|
||||
/// For `use` items these lints are:
|
||||
/// * ambiguous_glob_reexports
|
||||
/// * dead_code
|
||||
/// * deprecated
|
||||
/// * hidden_glob_reexports
|
||||
/// * unreachable_pub
|
||||
/// * unused
|
||||
/// * unused_braces
|
||||
/// * unused_import_braces
|
||||
/// * clippy::disallowed_types
|
||||
/// * clippy::enum_glob_use
|
||||
/// * clippy::macro_use_imports
|
||||
/// * clippy::module_name_repetitions
|
||||
/// * clippy::redundant_pub_crate
|
||||
/// * clippy::single_component_path_imports
|
||||
/// * clippy::unsafe_removed_from_name
|
||||
/// * clippy::wildcard_imports
|
||||
///
|
||||
/// For `extern crate` items these lints are:
|
||||
/// * `unused_imports` on items with `#[macro_use]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USELESS_ATTRIBUTE,
|
||||
correctness,
|
||||
"use of lint attributes on `extern crate` items"
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version. Also allows "TBD" to signal
|
||||
/// future deprecation.
|
||||
/// Checks for attributes that allow lints without a reason.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
/// ### Why restrict this?
|
||||
/// Justifying each `allow` helps readers understand the reasoning,
|
||||
/// and may allow removing `allow` attributes if their purpose is obsolete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else() { /* ... */ }
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DEPRECATED_SEMVER,
|
||||
correctness,
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
restriction,
|
||||
"ensures that all `allow` and `expect` attributes have a reason"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -181,161 +135,6 @@
|
||||
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for attributes that allow lints without a reason.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Justifying each `allow` helps readers understand the reasoning,
|
||||
/// and may allow removing `allow` attributes if their purpose is obsolete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
restriction,
|
||||
"ensures that all `allow` and `expect` attributes have a reason"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The expected panic message should be specified to ensure that the test is actually
|
||||
/// panicking with the expected message, and not another unrelated panic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic = "attempt to divide by zero"]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.74.0"]
|
||||
pub SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
pedantic,
|
||||
"ensures that all `should_panic` attributes specify its expected panic message"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items with `#[repr(packed)]`-attribute without ABI qualification
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable.
|
||||
/// While this is fine as long as the type is accessed correctly within Rust-code, most uses
|
||||
/// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or
|
||||
/// other external specifications. In such situations, the unstable Rust-ABI implied in
|
||||
/// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change.
|
||||
///
|
||||
/// In case you are relying on a well defined and stable memory layout, qualify the type's
|
||||
/// representation using the `C`-ABI. Otherwise, if the type in question is only ever
|
||||
/// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[repr(packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[repr(C, packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(any(unix))]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(unix)]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub NON_MINIMAL_CFG,
|
||||
style,
|
||||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
|
||||
@@ -364,63 +163,23 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
|
||||
/// and suggests to replace it with `#[allow(clippy::lint)]`.
|
||||
/// Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version. Also allows "TBD" to signal
|
||||
/// future deprecation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
|
||||
/// run by anything else than clippy.
|
||||
/// For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else() { /* ... */ }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::deprecated_cfg_attr)]
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub UNNECESSARY_CLIPPY_CFG,
|
||||
suspicious,
|
||||
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items that have the same kind of attributes with mixed styles (inner/outer).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Having both style of said attributes makes it more complicated to read code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint currently has false-negatives when mixing same attributes
|
||||
/// but they have different path symbols, for example:
|
||||
/// ```ignore
|
||||
/// #[custom_attribute]
|
||||
/// pub fn foo() {
|
||||
/// #![my_crate::custom_attribute]
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// pub fn foo() {
|
||||
/// #![cfg(windows)]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// #[cfg(windows)]
|
||||
/// pub fn foo() {
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MIXED_ATTRIBUTES_STYLE,
|
||||
style,
|
||||
"item has both inner and outer attributes"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DEPRECATED_SEMVER,
|
||||
correctness,
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -478,15 +237,272 @@
|
||||
"ignored tests without messages"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INLINE_ALWAYS,
|
||||
pedantic,
|
||||
"use of `#[inline(always)]`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items that have the same kind of attributes with mixed styles (inner/outer).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Having both style of said attributes makes it more complicated to read code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint currently has false-negatives when mixing same attributes
|
||||
/// but they have different path symbols, for example:
|
||||
/// ```ignore
|
||||
/// #[custom_attribute]
|
||||
/// pub fn foo() {
|
||||
/// #![my_crate::custom_attribute]
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// pub fn foo() {
|
||||
/// #![cfg(windows)]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// #[cfg(windows)]
|
||||
/// pub fn foo() {
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MIXED_ATTRIBUTES_STYLE,
|
||||
style,
|
||||
"item has both inner and outer attributes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(any(unix))]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(unix)]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub NON_MINIMAL_CFG,
|
||||
style,
|
||||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items with `#[repr(packed)]`-attribute without ABI qualification
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable.
|
||||
/// While this is fine as long as the type is accessed correctly within Rust-code, most uses
|
||||
/// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or
|
||||
/// other external specifications. In such situations, the unstable Rust-ABI implied in
|
||||
/// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change.
|
||||
///
|
||||
/// In case you are relying on a well defined and stable memory layout, qualify the type's
|
||||
/// representation using the `C`-ABI. Otherwise, if the type in question is only ever
|
||||
/// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[repr(packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[repr(C, packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The expected panic message should be specified to ensure that the test is actually
|
||||
/// panicking with the expected message, and not another unrelated panic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic = "attempt to divide by zero"]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.74.0"]
|
||||
pub SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
pedantic,
|
||||
"ensures that all `should_panic` attributes specify its expected panic message"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
|
||||
/// and suggests to replace it with `#[allow(clippy::lint)]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
|
||||
/// run by anything else than clippy.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::deprecated_cfg_attr)]
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub UNNECESSARY_CLIPPY_CFG,
|
||||
suspicious,
|
||||
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint permits lint attributes for lints emitted on the items themself.
|
||||
/// For `use` items these lints are:
|
||||
/// * ambiguous_glob_reexports
|
||||
/// * dead_code
|
||||
/// * deprecated
|
||||
/// * hidden_glob_reexports
|
||||
/// * unreachable_pub
|
||||
/// * unused
|
||||
/// * unused_braces
|
||||
/// * unused_import_braces
|
||||
/// * clippy::disallowed_types
|
||||
/// * clippy::enum_glob_use
|
||||
/// * clippy::macro_use_imports
|
||||
/// * clippy::module_name_repetitions
|
||||
/// * clippy::redundant_pub_crate
|
||||
/// * clippy::single_component_path_imports
|
||||
/// * clippy::unsafe_removed_from_name
|
||||
/// * clippy::wildcard_imports
|
||||
///
|
||||
/// For `extern crate` items these lints are:
|
||||
/// * `unused_imports` on items with `#[macro_use]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USELESS_ATTRIBUTE,
|
||||
correctness,
|
||||
"use of lint attributes on `extern crate` items"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]);
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CFG_ATTR,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
NON_MINIMAL_CFG,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl_lint_pass!(PostExpansionEarlyAttributes => [
|
||||
ALLOW_ATTRIBUTES,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
DEPRECATED_SEMVER,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
USELESS_ATTRIBUTE,
|
||||
]);
|
||||
|
||||
pub struct Attributes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Attributes => [
|
||||
INLINE_ALWAYS,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
]);
|
||||
|
||||
impl Attributes {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
@@ -529,13 +545,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CFG_ATTR,
|
||||
NON_MINIMAL_CFG,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
deprecated_cfg_attr::check(cx, attr, &self.msrv);
|
||||
@@ -558,18 +567,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(PostExpansionEarlyAttributes => [
|
||||
ALLOW_ATTRIBUTES,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
DEPRECATED_SEMVER,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
USELESS_ATTRIBUTE,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for PostExpansionEarlyAttributes {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
|
||||
blanket_clippy_restriction_lints::check_command_line(cx);
|
||||
|
||||
@@ -11,6 +11,43 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Allows users to configure types which should not be held across await
|
||||
/// suspension points.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There are some types which are perfectly safe to use concurrently from
|
||||
/// a memory access perspective, but that will cause bugs at runtime if
|
||||
/// they are held in such a way.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
/// await-holding-invalid-types = [
|
||||
/// # You can specify a type name
|
||||
/// "CustomLockType",
|
||||
/// # You can (optionally) specify a reason
|
||||
/// { path = "OtherCustomLockType", reason = "Relies on a thread local" }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # async fn baz() {}
|
||||
/// struct CustomLockType;
|
||||
/// struct OtherCustomLockType;
|
||||
/// async fn foo() {
|
||||
/// let _x = CustomLockType;
|
||||
/// let _y = OtherCustomLockType;
|
||||
/// baz().await; // Lint violation
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub AWAIT_HOLDING_INVALID_TYPE,
|
||||
suspicious,
|
||||
"holding a type across an await point which is not allowed to be held as per the configuration"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `await` while holding a non-async-aware
|
||||
@@ -135,44 +172,11 @@
|
||||
"inside an async function, holding a `RefCell` ref while calling `await`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Allows users to configure types which should not be held across await
|
||||
/// suspension points.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There are some types which are perfectly safe to use concurrently from
|
||||
/// a memory access perspective, but that will cause bugs at runtime if
|
||||
/// they are held in such a way.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
/// await-holding-invalid-types = [
|
||||
/// # You can specify a type name
|
||||
/// "CustomLockType",
|
||||
/// # You can (optionally) specify a reason
|
||||
/// { path = "OtherCustomLockType", reason = "Relies on a thread local" }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # async fn baz() {}
|
||||
/// struct CustomLockType;
|
||||
/// struct OtherCustomLockType;
|
||||
/// async fn foo() {
|
||||
/// let _x = CustomLockType;
|
||||
/// let _y = OtherCustomLockType;
|
||||
/// baz().await; // Lint violation
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub AWAIT_HOLDING_INVALID_TYPE,
|
||||
suspicious,
|
||||
"holding a type across an await point which is not allowed to be held as per the configuration"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
|
||||
impl_lint_pass!(AwaitHolding => [
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
AWAIT_HOLDING_LOCK,
|
||||
AWAIT_HOLDING_REFCELL_REF,
|
||||
]);
|
||||
|
||||
pub struct AwaitHolding {
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
pedantic,
|
||||
"using if to convert bool to int"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
"boolean expressions that contain terminals which can be eliminated"
|
||||
}
|
||||
|
||||
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
|
||||
|
||||
// For each pairs, both orders are considered.
|
||||
const METHODS_WITH_NEGATION: [(Option<RustcVersion>, Symbol, Symbol); 3] = [
|
||||
(None, sym::is_some, sym::is_none),
|
||||
@@ -91,8 +93,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_default_equivalent, sym};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::ty::expr_sig;
|
||||
use clippy_utils::{is_default_equivalent, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
style,
|
||||
"hard to read byte char slice"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
|
||||
|
||||
impl EarlyLintPass for ByteCharSlice {
|
||||
|
||||
+127
-127
@@ -56,126 +56,6 @@
|
||||
"common metadata is defined in `Cargo.toml`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These prefixes and suffixes have no significant meaning.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with feature name redundancy
|
||||
/// [features]
|
||||
/// default = ["use-abc", "with-def", "ghi-support"]
|
||||
/// use-abc = [] // redundant
|
||||
/// with-def = [] // redundant
|
||||
/// ghi-support = [] // redundant
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def", "ghi"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
/// ghi = []
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub REDUNDANT_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a redundant feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for negative feature names with prefix `no-` or `not-`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Features are supposed to be additive, and negatively-named features violate it.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with negative feature names
|
||||
/// [features]
|
||||
/// default = []
|
||||
/// no-abc = []
|
||||
/// not-def = []
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
///
|
||||
/// ```
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub NEGATIVE_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a negative feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
/// In those cases, you can allow that specific crate using
|
||||
/// the `allowed-duplicate-crates` configuration option.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "=3.1.0"
|
||||
/// ansi_term = "=0.11.0"
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MULTIPLE_CRATE_VERSIONS,
|
||||
cargo,
|
||||
"multiple versions of the same crate being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for wildcard dependencies in the `Cargo.toml`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
|
||||
/// it is highly unlikely that you work with any possible version of your dependency,
|
||||
/// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// regex = "*"
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// # allow patch updates, but not minor or major version changes
|
||||
/// some_crate_1 = "~1.2.3"
|
||||
///
|
||||
/// # pin the version to a specific version
|
||||
/// some_crate_2 = "=1.2.3"
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub WILDCARD_DEPENDENCIES,
|
||||
cargo,
|
||||
"wildcard dependencies being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lint groups with the same priority as lints in the `Cargo.toml`
|
||||
@@ -213,20 +93,140 @@
|
||||
"a lint group in `Cargo.toml` at the same priority as a lint"
|
||||
}
|
||||
|
||||
pub struct Cargo {
|
||||
allowed_duplicate_crates: FxHashSet<String>,
|
||||
ignore_publish: bool,
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
/// In those cases, you can allow that specific crate using
|
||||
/// the `allowed-duplicate-crates` configuration option.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "=3.1.0"
|
||||
/// ansi_term = "=0.11.0"
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MULTIPLE_CRATE_VERSIONS,
|
||||
cargo,
|
||||
"multiple versions of the same crate being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for negative feature names with prefix `no-` or `not-`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Features are supposed to be additive, and negatively-named features violate it.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with negative feature names
|
||||
/// [features]
|
||||
/// default = []
|
||||
/// no-abc = []
|
||||
/// not-def = []
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
///
|
||||
/// ```
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub NEGATIVE_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a negative feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These prefixes and suffixes have no significant meaning.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with feature name redundancy
|
||||
/// [features]
|
||||
/// default = ["use-abc", "with-def", "ghi-support"]
|
||||
/// use-abc = [] // redundant
|
||||
/// with-def = [] // redundant
|
||||
/// ghi-support = [] // redundant
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def", "ghi"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
/// ghi = []
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub REDUNDANT_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a redundant feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for wildcard dependencies in the `Cargo.toml`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
|
||||
/// it is highly unlikely that you work with any possible version of your dependency,
|
||||
/// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// regex = "*"
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// # allow patch updates, but not minor or major version changes
|
||||
/// some_crate_1 = "~1.2.3"
|
||||
///
|
||||
/// # pin the version to a specific version
|
||||
/// some_crate_2 = "=1.2.3"
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub WILDCARD_DEPENDENCIES,
|
||||
cargo,
|
||||
"wildcard dependencies being used"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Cargo => [
|
||||
CARGO_COMMON_METADATA,
|
||||
REDUNDANT_FEATURE_NAMES,
|
||||
NEGATIVE_FEATURE_NAMES,
|
||||
MULTIPLE_CRATE_VERSIONS,
|
||||
WILDCARD_DEPENDENCIES,
|
||||
LINT_GROUPS_PRIORITY,
|
||||
MULTIPLE_CRATE_VERSIONS,
|
||||
NEGATIVE_FEATURE_NAMES,
|
||||
REDUNDANT_FEATURE_NAMES,
|
||||
WILDCARD_DEPENDENCIES,
|
||||
]);
|
||||
|
||||
pub struct Cargo {
|
||||
allowed_duplicate_crates: FxHashSet<String>,
|
||||
ignore_publish: bool,
|
||||
}
|
||||
|
||||
impl Cargo {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
+505
-505
@@ -36,50 +36,231 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from any numeric type to a float type where
|
||||
/// the receiving type cannot store all values from the original type without
|
||||
/// rounding errors. This possible rounding is to be expected, so this lint is
|
||||
/// `Allow` by default.
|
||||
/// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type.
|
||||
///
|
||||
/// Basically, this warns on casting any integer with 32 or more bits to `f32`
|
||||
/// or any 64-bit integer to `f64`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad at all. But in some applications it can be
|
||||
/// helpful to know where precision loss can take place. This lint can help find
|
||||
/// those places in the code.
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include a dangerous cast that might go undetected due to the type being inferred.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = u64::MAX;
|
||||
/// x as f64;
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// // BUG: `t` is already a reference, so we will here
|
||||
/// // return a dangling pointer to a temporary value instead
|
||||
/// &t as *const _ as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_PRECISION_LOSS,
|
||||
pedantic,
|
||||
"casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// t as *const T as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub AS_POINTER_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from a signed to an unsigned numeric
|
||||
/// type. In this case, negative values wrap around to large positive values,
|
||||
/// which can be quite surprising in practice. However, since the cast works as
|
||||
/// defined, this lint is `Allow` by default.
|
||||
/// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Possibly surprising results. You can activate this lint
|
||||
/// as a one-time check to see where numeric wrapping can arise.
|
||||
/// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
|
||||
/// mutability is used, making it unlikely that having it as a mutable pointer is correct.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let y: i8 = -1;
|
||||
/// y as u64; // will return 18446744073709551615
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_ptr() as *mut u8;
|
||||
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_mut_ptr();
|
||||
/// unsafe { ptr.write(4) };
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub AS_PTR_CAST_MUT,
|
||||
nursery,
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include lossy conversion or a dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `&raw const` or
|
||||
/// `&raw mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `&raw` explanation in the Reference for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &raw const val;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &raw mut val_mut;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `abs()` method that cast the result to unsigned.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `unsigned_abs()` method avoids panic when called on the MIN value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.abs() as u32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.unsigned_abs();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub CAST_ABS_TO_UNSIGNED,
|
||||
suspicious,
|
||||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum tuple constructor to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The cast is easily confused with casting a c-like enum value to an integer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X(i32) };
|
||||
/// let _ = E::X as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_CONSTRUCTOR,
|
||||
suspicious,
|
||||
"casts from an enum tuple constructor to an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum type to an integral type that will definitely truncate the
|
||||
/// value.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The resulting integral value will not match the value of the variant it came from.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X = 256 };
|
||||
/// let _ = E::X as u8;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_TRUNCATION,
|
||||
suspicious,
|
||||
"casts from an enum type to an integral type that will truncate the value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts between numeric types that can be replaced by safe
|
||||
/// conversion functions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust's `as` keyword will perform many kinds of conversions, including
|
||||
/// silently lossy conversions. Conversion functions such as `i32::from`
|
||||
/// will only perform lossless conversions. Using the conversion functions
|
||||
/// prevents conversions from becoming silently lossy if the input types
|
||||
/// ever change, and makes it clear for people reading the code that the
|
||||
/// conversion is lossless.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// x as u64
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `::from` would look like this:
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// u64::from(x)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_SIGN_LOSS,
|
||||
pub CAST_LOSSLESS,
|
||||
pedantic,
|
||||
"casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
|
||||
"casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a known NaN float being cast to an integer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaNs are cast into zero, so one could simply use this and make the
|
||||
/// code more readable. The lint could also hint at a programmer error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _ = (0.0_f32 / 0.0) as u64;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _ = 0_u64;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub CAST_NAN_TO_INT,
|
||||
suspicious,
|
||||
"casting a known floating-point NaN into an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -159,71 +340,28 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts between numeric types that can be replaced by safe
|
||||
/// conversion functions.
|
||||
/// Checks for casts from any numeric type to a float type where
|
||||
/// the receiving type cannot store all values from the original type without
|
||||
/// rounding errors. This possible rounding is to be expected, so this lint is
|
||||
/// `Allow` by default.
|
||||
///
|
||||
/// Basically, this warns on casting any integer with 32 or more bits to `f32`
|
||||
/// or any 64-bit integer to `f64`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust's `as` keyword will perform many kinds of conversions, including
|
||||
/// silently lossy conversions. Conversion functions such as `i32::from`
|
||||
/// will only perform lossless conversions. Using the conversion functions
|
||||
/// prevents conversions from becoming silently lossy if the input types
|
||||
/// ever change, and makes it clear for people reading the code that the
|
||||
/// conversion is lossless.
|
||||
/// It's not bad at all. But in some applications it can be
|
||||
/// helpful to know where precision loss can take place. This lint can help find
|
||||
/// those places in the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// x as u64
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `::from` would look like this:
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// u64::from(x)
|
||||
/// }
|
||||
/// let x = u64::MAX;
|
||||
/// x as f64;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_LOSSLESS,
|
||||
pub CAST_PRECISION_LOSS,
|
||||
pedantic,
|
||||
"casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts to the same type, casts of int literals to integer
|
||||
/// types, casts of float literals to float types, and casts between raw
|
||||
/// pointers that don't change type or constness.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's just unnecessary.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// When the expression on the left is a function call, the lint considers
|
||||
/// the return type to be a type alias if it's aliased through a `use`
|
||||
/// statement (like `use std::io::Result as IoResult`). It will not lint
|
||||
/// such cases.
|
||||
///
|
||||
/// This check will only work on primitive types without any intermediate
|
||||
/// references: raw pointers and trait objects may or may not work.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = 2i32 as i32;
|
||||
/// let _ = 0.5 as f32;
|
||||
/// ```
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```no_run
|
||||
/// let _ = 2_i32;
|
||||
/// let _ = 0.5_f32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
"cast to the same type, e.g., `x as i32` where `x: i32`"
|
||||
"casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -254,6 +392,154 @@
|
||||
"cast from a pointer to a more strictly aligned pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from a signed to an unsigned numeric
|
||||
/// type. In this case, negative values wrap around to large positive values,
|
||||
/// which can be quite surprising in practice. However, since the cast works as
|
||||
/// defined, this lint is `Allow` by default.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Possibly surprising results. You can activate this lint
|
||||
/// as a one-time check to see where numeric wrapping can arise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let y: i8 = -1;
|
||||
/// y as u64; // will return 18446744073709551615
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_SIGN_LOSS,
|
||||
pedantic,
|
||||
"casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The produced raw pointer to a slice does not update its length metadata. The produced
|
||||
/// pointer will point to a different number of bytes than the original pointer because the
|
||||
/// length metadata of a raw slice pointer is in elements rather than bytes.
|
||||
/// Producing a slice reference from the raw pointer will either create a slice with
|
||||
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// // Missing data
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let p = &a as *const [i32] as *const [u8];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// // Undefined Behavior (note: also potential alignment issues)
|
||||
/// ```no_run
|
||||
/// let a = [1_u8, 2, 3, 4];
|
||||
/// let p = &a as *const [u8] as *const [u32];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let old_ptr = &a as *const [i32];
|
||||
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
|
||||
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
|
||||
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*new_ptr);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_SLICE_DIFFERENT_SIZES,
|
||||
correctness,
|
||||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a raw slice being cast to a slice pointer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can result in multiple `&mut` references to the same location when only a pointer is
|
||||
/// required.
|
||||
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
|
||||
/// the same [safety requirements] to be upheld.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
|
||||
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
|
||||
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
/// ```
|
||||
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub CAST_SLICE_FROM_RAW_PARTS,
|
||||
suspicious,
|
||||
"casting a slice created from a pointer and length to a slice pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions where a character literal is cast
|
||||
/// to `u8` and suggests using a byte literal instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In general, casting values to smaller types is
|
||||
/// error-prone and should be avoided where possible. In the particular case of
|
||||
/// converting a character literal to `u8`, it is easy to avoid by just using a
|
||||
/// byte literal instead. As an added bonus, `b'a'` is also slightly shorter
|
||||
/// than `'a' as u8`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// 'x' as u8
|
||||
/// ```
|
||||
///
|
||||
/// A better version, using the byte literal:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// b'x'
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CHAR_LIT_AS_U8,
|
||||
complexity,
|
||||
"casting a character literal to `u8` truncates"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a primitive method pointer like `max`/`min` to any integer type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = u16::max as usize;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = u16::MAX as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
suspicious,
|
||||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of function pointers to something other than `usize`.
|
||||
@@ -284,39 +570,6 @@
|
||||
"casting a function pointer to a numeric type other than `usize`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a function pointer to a numeric type not wide enough to
|
||||
/// store an address.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such a cast discards some bits of the function's address. If this is intended, it would be more
|
||||
/// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with
|
||||
/// a comment) to perform the truncation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as i32;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let fn_ptr = fn1 as usize;
|
||||
/// let fn_ptr_truncated = fn_ptr as i32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
style,
|
||||
"casting a function pointer to a numeric type not wide enough to store the address"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a function pointer to any integer type.
|
||||
@@ -361,30 +614,87 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions where a character literal is cast
|
||||
/// to `u8` and suggests using a byte literal instead.
|
||||
/// Checks for casts of a function pointer to a numeric type not wide enough to
|
||||
/// store an address.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In general, casting values to smaller types is
|
||||
/// error-prone and should be avoided where possible. In the particular case of
|
||||
/// converting a character literal to `u8`, it is easy to avoid by just using a
|
||||
/// byte literal instead. As an added bonus, `b'a'` is also slightly shorter
|
||||
/// than `'a' as u8`.
|
||||
/// Such a cast discards some bits of the function's address. If this is intended, it would be more
|
||||
/// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with
|
||||
/// a comment) to perform the truncation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// 'x' as u8
|
||||
/// ```no_run
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as i32;
|
||||
/// ```
|
||||
///
|
||||
/// A better version, using the byte literal:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// b'x'
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let fn_ptr = fn1 as usize;
|
||||
/// let fn_ptr_truncated = fn_ptr as i32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CHAR_LIT_AS_U8,
|
||||
complexity,
|
||||
"casting a character literal to `u8` truncates"
|
||||
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
style,
|
||||
"casting a function pointer to a numeric type not wide enough to store the address"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a binding is always cast to a different type when used, it would be clearer
|
||||
/// and more efficient to define it with the target type from the start.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// const SIZE: u16 = 15;
|
||||
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// const SIZE: usize = 15;
|
||||
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||
/// ```
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub NEEDLESS_TYPE_CAST,
|
||||
nursery,
|
||||
"binding defined with one type but always cast to another"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -455,243 +765,62 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum type to an integral type that will definitely truncate the
|
||||
/// value.
|
||||
/// Checks for casts of references to pointer using `as`
|
||||
/// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The resulting integral value will not match the value of the variant it came from.
|
||||
/// Using `as` casts may result in silently changing mutability or type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X = 256 };
|
||||
/// let _ = E::X as u8;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_TRUNCATION,
|
||||
suspicious,
|
||||
"casts from an enum type to an integral type that will truncate the value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The produced raw pointer to a slice does not update its length metadata. The produced
|
||||
/// pointer will point to a different number of bytes than the original pointer because the
|
||||
/// length metadata of a raw slice pointer is in elements rather than bytes.
|
||||
/// Producing a slice reference from the raw pointer will either create a slice with
|
||||
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// // Missing data
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let p = &a as *const [i32] as *const [u8];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// // Undefined Behavior (note: also potential alignment issues)
|
||||
/// ```no_run
|
||||
/// let a = [1_u8, 2, 3, 4];
|
||||
/// let p = &a as *const [u8] as *const [u32];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let old_ptr = &a as *const [i32];
|
||||
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
|
||||
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
|
||||
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*new_ptr);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_SLICE_DIFFERENT_SIZES,
|
||||
correctness,
|
||||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum tuple constructor to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The cast is easily confused with casting a c-like enum value to an integer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X(i32) };
|
||||
/// let _ = E::X as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_CONSTRUCTOR,
|
||||
suspicious,
|
||||
"casts from an enum tuple constructor to an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `abs()` method that cast the result to unsigned.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `unsigned_abs()` method avoids panic when called on the MIN value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.abs() as u32;
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = a_ref as *const _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.unsigned_abs();
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = std::ptr::from_ref(a_ref);
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub CAST_ABS_TO_UNSIGNED,
|
||||
suspicious,
|
||||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include lossy conversion or a dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `&raw const` or
|
||||
/// `&raw mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `&raw` explanation in the Reference for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &raw const val;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &raw mut val_mut;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub REF_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
"using `as` to cast a reference to pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a raw slice being cast to a slice pointer
|
||||
/// Checks for casts to the same type, casts of int literals to integer
|
||||
/// types, casts of float literals to float types, and casts between raw
|
||||
/// pointers that don't change type or constness.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can result in multiple `&mut` references to the same location when only a pointer is
|
||||
/// required.
|
||||
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
|
||||
/// the same [safety requirements] to be upheld.
|
||||
/// It's just unnecessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
|
||||
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
|
||||
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
/// ```
|
||||
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub CAST_SLICE_FROM_RAW_PARTS,
|
||||
suspicious,
|
||||
"casting a slice created from a pointer and length to a slice pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer.
|
||||
/// ### Known problems
|
||||
/// When the expression on the left is a function call, the lint considers
|
||||
/// the return type to be a type alias if it's aliased through a `use`
|
||||
/// statement (like `use std::io::Result as IoResult`). It will not lint
|
||||
/// such cases.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
|
||||
/// mutability is used, making it unlikely that having it as a mutable pointer is correct.
|
||||
/// This check will only work on primitive types without any intermediate
|
||||
/// references: raw pointers and trait objects may or may not work.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_ptr() as *mut u8;
|
||||
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
|
||||
/// let _ = 2i32 as i32;
|
||||
/// let _ = 0.5 as f32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_mut_ptr();
|
||||
/// unsafe { ptr.write(4) };
|
||||
/// let _ = 2_i32;
|
||||
/// let _ = 0.5_f32;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub AS_PTR_CAST_MUT,
|
||||
nursery,
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a known NaN float being cast to an integer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaNs are cast into zero, so one could simply use this and make the
|
||||
/// code more readable. The lint could also hint at a programmer error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _ = (0.0_f32 / 0.0) as u64;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _ = 0_u64;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub CAST_NAN_TO_INT,
|
||||
suspicious,
|
||||
"casting a known floating-point NaN into an integer"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
"cast to the same type, e.g., `x as i32` where `x: i32`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -717,134 +846,36 @@
|
||||
"using `0 as *{const, mut} T`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of references to pointer using `as`
|
||||
/// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `as` casts may result in silently changing mutability or type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = a_ref as *const _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = std::ptr::from_ref(a_ref);
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub REF_AS_PTR,
|
||||
pedantic,
|
||||
"using `as` to cast a reference to pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include a dangerous cast that might go undetected due to the type being inferred.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// // BUG: `t` is already a reference, so we will here
|
||||
/// // return a dangling pointer to a temporary value instead
|
||||
/// &t as *const _ as usize
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// t as *const T as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub AS_POINTER_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a primitive method pointer like `max`/`min` to any integer type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = u16::max as usize;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = u16::MAX as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
suspicious,
|
||||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a binding is always cast to a different type when used, it would be clearer
|
||||
/// and more efficient to define it with the target type from the start.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// const SIZE: u16 = 15;
|
||||
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// const SIZE: usize = 15;
|
||||
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||
/// ```
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub NEEDLESS_TYPE_CAST,
|
||||
nursery,
|
||||
"binding defined with one type but always cast to another"
|
||||
}
|
||||
impl_lint_pass!(Casts => [
|
||||
AS_POINTER_UNDERSCORE,
|
||||
AS_PTR_CAST_MUT,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_LOSSLESS,
|
||||
CAST_NAN_TO_INT,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
CAST_PRECISION_LOSS,
|
||||
CAST_PTR_ALIGNMENT,
|
||||
CAST_SIGN_LOSS,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
CHAR_LIT_AS_U8,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST_ANY,
|
||||
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
MANUAL_DANGLING_PTR,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
PTR_AS_PTR,
|
||||
PTR_CAST_CONSTNESS,
|
||||
REF_AS_PTR,
|
||||
UNNECESSARY_CAST,
|
||||
ZERO_PTR,
|
||||
]);
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
@@ -856,37 +887,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Casts => [
|
||||
CAST_PRECISION_LOSS,
|
||||
CAST_SIGN_LOSS,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
CAST_LOSSLESS,
|
||||
CAST_PTR_ALIGNMENT,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
UNNECESSARY_CAST,
|
||||
FN_TO_NUMERIC_CAST_ANY,
|
||||
FN_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
CHAR_LIT_AS_U8,
|
||||
PTR_AS_PTR,
|
||||
PTR_CAST_CONSTNESS,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
AS_PTR_CAST_MUT,
|
||||
CAST_NAN_TO_INT,
|
||||
ZERO_PTR,
|
||||
REF_AS_PTR,
|
||||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.in_external_macro(cx.sess().source_map()) {
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"`try_from` could replace manual bounds checking when casting"
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
pub struct CheckedConversions {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -44,8 +46,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
impl LateLintPass<'_> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if let ExprKind::Binary(op, lhs, rhs) = item.kind
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
"cloning a reference for slice references"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
|
||||
|
||||
pub struct ClonedRefToSliceRefs<'a> {
|
||||
msrv: &'a Msrv,
|
||||
}
|
||||
@@ -53,8 +55,6 @@ pub fn new(conf: &'a Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if self.msrv.meets(cx, {
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
nursery,
|
||||
"coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
@eval_always = true
|
||||
}
|
||||
|
||||
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
||||
|
||||
pub struct CognitiveComplexity {
|
||||
limit: LimitStack,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
||||
|
||||
impl CognitiveComplexity {
|
||||
fn check<'tcx>(
|
||||
&self,
|
||||
|
||||
@@ -12,38 +12,6 @@
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// // …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x && y {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for collapsible `else { if ... }` expressions
|
||||
@@ -80,6 +48,40 @@
|
||||
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// // …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x && y {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]);
|
||||
|
||||
pub struct CollapsibleIf {
|
||||
msrv: Msrv,
|
||||
lint_commented_code: bool,
|
||||
@@ -259,8 +261,6 @@ fn check_significant_tokens_and_expect_attrs(
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
||||
|
||||
impl LateLintPass<'_> for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::If(cond, then, else_) = &expr.kind
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{get_enclosing_block, sym};
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::visitors::{Visitable, for_each_expr};
|
||||
use clippy_utils::{get_enclosing_block, sym};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@@ -40,6 +40,7 @@
|
||||
nursery,
|
||||
"a collection is never queried"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
suspicious,
|
||||
"using `crate` in a macro definition"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
|
||||
|
||||
impl EarlyLintPass for CrateInMacroDef {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_in_test, sym};
|
||||
use clippy_utils::macros::{MacroCall, macro_backtrace};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_in_test, sym};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
|
||||
@@ -33,6 +33,8 @@
|
||||
"`dbg!` macro is intended as a debugging tool"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
pub struct DbgMacro {
|
||||
allow_dbg_in_tests: bool,
|
||||
/// Tracks the `dbg!` macro callsites that are already checked.
|
||||
@@ -41,8 +43,6 @@ pub struct DbgMacro {
|
||||
prev_ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
impl DbgMacro {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
DbgMacro {
|
||||
|
||||
@@ -69,14 +69,14 @@
|
||||
"binding initialized with Default should have its fields set in the initializer"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Default {
|
||||
// Spans linted by `field_reassign_with_default`.
|
||||
reassigned_linted: FxHashSet<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion()
|
||||
|
||||
@@ -45,7 +45,10 @@
|
||||
complexity,
|
||||
"unit structs can be constructed without calling `default`"
|
||||
}
|
||||
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
|
||||
|
||||
declare_lint_pass!(DefaultConstructedUnitStructs => [
|
||||
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
]);
|
||||
|
||||
fn is_alias(ty: hir::Ty<'_>) -> bool {
|
||||
if let hir::TyKind::Path(ref qpath) = ty.kind {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
style,
|
||||
"check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
restriction,
|
||||
"unions without a `#[repr(C)]` attribute"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs};
|
||||
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym};
|
||||
use clippy_utils::{
|
||||
DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym,
|
||||
};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
@@ -20,6 +22,29 @@
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for dereferencing expressions which would be covered by auto-deref.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This unnecessarily complicates the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &*x;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &x;
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub EXPLICIT_AUTO_DEREF,
|
||||
complexity,
|
||||
"dereferencing when the compiler would automatically dereference"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit `deref()` or `deref_mut()` method calls.
|
||||
@@ -117,34 +142,11 @@
|
||||
"`ref` binding to a reference"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for dereferencing expressions which would be covered by auto-deref.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This unnecessarily complicates the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &*x;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &x;
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub EXPLICIT_AUTO_DEREF,
|
||||
complexity,
|
||||
"dereferencing when the compiler would automatically dereference"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Dereferencing<'_> => [
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
EXPLICIT_DEREF_METHODS,
|
||||
NEEDLESS_BORROW,
|
||||
REF_BINDING_TO_REFERENCE,
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
]);
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"manual implementation of the `Default` trait which is equal to a derive"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
|
||||
|
||||
pub struct DerivableImpls {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -66,8 +68,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
|
||||
|
||||
fn is_path_self(e: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
|
||||
matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
|
||||
|
||||
@@ -10,36 +10,6 @@
|
||||
mod expl_impl_clone_on_copy;
|
||||
mod unsafe_derive_deserialize;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialEq` implementations for types with a derived `Hash`
|
||||
/// implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```text
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
correctness,
|
||||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
|
||||
@@ -91,6 +61,68 @@
|
||||
"deriving `Ord` but implementing `PartialOrd` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for types that derive `PartialEq` and could implement `Eq`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
|
||||
/// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
|
||||
/// in APIs that require `Eq` types. It also allows structs containing `T` to derive
|
||||
/// `Eq` themselves.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq, Eq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
nursery,
|
||||
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialEq` implementations for types with a derived `Hash`
|
||||
/// implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```text
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
correctness,
|
||||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit `Clone` implementations for `Copy`
|
||||
@@ -152,44 +184,12 @@
|
||||
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for types that derive `PartialEq` and could implement `Eq`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
|
||||
/// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
|
||||
/// in APIs that require `Eq` types. It also allows structs containing `T` to derive
|
||||
/// `Eq` themselves.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq, Eq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
nursery,
|
||||
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Derive => [
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
UNSAFE_DERIVE_DESERIALIZE,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"declaration of a disallowed field use"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]);
|
||||
|
||||
pub struct DisallowedFields {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
@@ -74,8 +76,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedFields {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (id, span) = match &expr.kind {
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
"use of a disallowed macro"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
|
||||
|
||||
pub struct DisallowedMacros {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
seen: FxHashSet<ExpnId>,
|
||||
@@ -125,8 +127,6 @@ fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
|
||||
|
||||
impl LateLintPass<'_> for DisallowedMacros {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
// once we check a crate in the late pass we can emit the early pass lints
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
"use of a disallowed method call"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
||||
|
||||
pub struct DisallowedMethods {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
@@ -84,8 +86,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.desugaring_kind().is_some() {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"usage of a disallowed/placeholder name"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
||||
|
||||
pub struct DisallowedNames {
|
||||
disallow: FxHashSet<Symbol>,
|
||||
}
|
||||
@@ -38,8 +40,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
if let PatKind::Binding(.., ident, _) = pat.kind
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
"usage of non-allowed Unicode scripts"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
|
||||
|
||||
pub struct DisallowedScriptIdents {
|
||||
whitelist: FxHashSet<Script>,
|
||||
}
|
||||
@@ -62,8 +64,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
|
||||
|
||||
impl EarlyLintPass for DisallowedScriptIdents {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
||||
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
"use of disallowed types"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
pub struct DisallowedTypes {
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath)>,
|
||||
@@ -104,8 +106,6 @@ pub fn def_kind_predicate(def_kind: DefKind) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind
|
||||
|
||||
@@ -94,17 +94,14 @@ fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
|
||||
Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link)
|
||||
if no_report_depth == 0 && !offset.is_empty() =>
|
||||
{
|
||||
if doc_string[..offset.end]
|
||||
.trim_end()
|
||||
.ends_with(TERMINAL_PUNCTUATION_MARKS)
|
||||
{
|
||||
if trim_trailing_symbols(&doc_string[..offset.end]).ends_with(TERMINAL_PUNCTUATION_MARKS) {
|
||||
current_paragraph = None;
|
||||
} else {
|
||||
current_paragraph = Some(MissingPunctuation::Fixable(offset.end));
|
||||
}
|
||||
},
|
||||
Event::Text(..) if no_report_depth == 0 && !offset.is_empty() => {
|
||||
let trimmed = doc_string[..offset.end].trim_end();
|
||||
let trimmed = trim_trailing_symbols(&doc_string[..offset.end]);
|
||||
if trimmed.ends_with(TERMINAL_PUNCTUATION_MARKS) {
|
||||
current_paragraph = None;
|
||||
} else if let Some(t) = trimmed.strip_suffix(|c| c == ')' || c == '"') {
|
||||
@@ -125,6 +122,21 @@ fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
|
||||
missing_punctuation
|
||||
}
|
||||
|
||||
fn trim_trailing_symbols(s: &str) -> &str {
|
||||
s.trim_end_matches(|c: char|
|
||||
// Source: https://unicodeplus.com
|
||||
matches!(c as u32,
|
||||
0x1F300..=0x1F5FF | // Miscellaneous Symbols and Pictographs
|
||||
0x1F600..=0x1F64F | // Emoticons
|
||||
0x1F900..=0x1F9FF | // Supplemental Symbols and Pictographs
|
||||
0x2700..=0x27BF | // Dingbats
|
||||
0x1FA70..=0x1FAFF | // Symbols and Pictographs Extended-A
|
||||
0x1F680..=0x1F6FF | // Transport and Map Symbols
|
||||
0x2600..=0x26FF | // Miscellaneous Symbols
|
||||
0xFE00..=0xFE0F // Variation selectors
|
||||
) || c.is_whitespace())
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum MissingPunctuation {
|
||||
Fixable(usize),
|
||||
|
||||
+416
-416
@@ -39,6 +39,198 @@
|
||||
mod test_attr_in_doctest;
|
||||
mod too_long_first_doc_paragraph;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the doc comments have unbroken links, mostly caused
|
||||
/// by bad formatted links such as broken across multiple lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Because documentation generated by rustdoc will be broken
|
||||
/// since expected links won't be links and just text.
|
||||
///
|
||||
/// ### Examples
|
||||
/// This link is broken:
|
||||
/// ```no_run
|
||||
/// /// [example of a bad link](https://
|
||||
/// /// github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
///
|
||||
/// It shouldn't be broken across multiple lines to work:
|
||||
/// ```no_run
|
||||
/// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.90.0"]
|
||||
pub DOC_BROKEN_LINK,
|
||||
pedantic,
|
||||
"broken document link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
|
||||
/// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
|
||||
/// is clearer in this regard.
|
||||
///
|
||||
/// ### Example
|
||||
/// The two replacement dots in this example represent a double space.
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and··
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and\
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
pedantic,
|
||||
"double-space used for doc comment linebreak instead of `\\`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if included files in doc comments are included only for `cfg(doc)`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// These files are not useful for compilation but will still be included.
|
||||
/// Also, if any of these non-source code file is updated, it will trigger a
|
||||
/// recompilation.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// Excluding this will currently result in the file being left out if
|
||||
/// the item's docs are inlined from another crate. This may be fixed in a
|
||||
/// future version of rustdoc.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #![doc = include_str!("some_file.md")]
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_INCLUDE_WITHOUT_CFG,
|
||||
restriction,
|
||||
"check if files included in documentation are behind `cfg(doc)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// In CommonMark Markdown, the language used to write doc comments, a
|
||||
/// paragraph nested within a list or block quote does not need any line
|
||||
/// after the first one to be indented or marked. The specification calls
|
||||
/// this a "lazy paragraph continuation."
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This is easy to write but hard to read. Lazy continuations makes
|
||||
/// unintended markers hard to see, and make it harder to deduce the
|
||||
/// document's intended structure.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// This table is probably intended to have two rows,
|
||||
/// but it does not. It has zero rows, and is followed by
|
||||
/// a block quote.
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// >= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by escaping the marker:
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// \>= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// This example is actually intended to be a list:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by indenting the list contents:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub DOC_LAZY_CONTINUATION,
|
||||
style,
|
||||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for links with code directly adjacent to code text:
|
||||
/// `` [`MyItem`]`<`[`u32`]`>` ``.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be written more simply using HTML-style `<code>` tags.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! [`first`](x)`second`
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! <code>[first](x)second</code>
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_LINK_CODE,
|
||||
nursery,
|
||||
"link with code back-to-back with other code"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the presence of `_`, `::` or camel-case words
|
||||
@@ -83,59 +275,149 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for links with code directly adjacent to code text:
|
||||
/// `` [`MyItem`]`<`[`u32`]`>` ``.
|
||||
/// Warns if a link reference definition appears at the start of a
|
||||
/// list item or quote.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be written more simply using HTML-style `<code>` tags.
|
||||
/// This is probably intended as an intra-doc link. If it is really
|
||||
/// supposed to be a reference definition, it can be written outside
|
||||
/// of the list item or quote.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! [`first`](x)`second`
|
||||
/// //! - [link]: description
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! <code>[first](x)second</code>
|
||||
/// //! - [link][]: description (for intra-doc link)
|
||||
/// //!
|
||||
/// //! [link]: destination (for link reference definition)
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_LINK_CODE,
|
||||
nursery,
|
||||
"link with code back-to-back with other code"
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_NESTED_REFDEFS,
|
||||
suspicious,
|
||||
"link reference defined in list item or quote"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// unsafe functions and warns if there is no `# Safety` section.
|
||||
///
|
||||
/// Detects overindented list items in doc comments where the continuation
|
||||
/// lines are indented more than necessary.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unsafe functions should document their safety
|
||||
/// preconditions, so that users can be sure they are using them safely.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
/// Overindented list items in doc comments can lead to inconsistent and
|
||||
/// poorly formatted documentation when rendered. Excessive indentation may
|
||||
/// cause the text to be misinterpreted as a nested list item or code block,
|
||||
/// affecting readability and the overall structure of the documentation.
|
||||
///
|
||||
/// At least write a line about safety:
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.39.0"]
|
||||
pub MISSING_SAFETY_DOC,
|
||||
///
|
||||
/// Fixes this into:
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"`pub unsafe fn` without `# Safety` docs"
|
||||
"ensure list items are not overindented"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.
|
||||
/// Various Markdowns constructs are taken into account to avoid false positives.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A project may wish to enforce consistent doc comments by making sure paragraphs end with a
|
||||
/// punctuation mark.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Returns a random number
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// Returns a random number.
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll.
|
||||
/// ```
|
||||
///
|
||||
/// ### Terminal punctuation marks
|
||||
/// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.
|
||||
///
|
||||
/// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
|
||||
/// introduce a table or a list for example.
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
restriction,
|
||||
"missing terminal punctuation in doc comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects syntax that looks like a footnote reference.
|
||||
///
|
||||
/// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
|
||||
/// GFM does not parse a footnote reference unless its definition also
|
||||
/// exists. This lint checks for footnote references with missing
|
||||
/// definitions, unless it thinks you're writing a regex.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This probably means that a footnote was meant to exist,
|
||||
/// but was not written.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// This is not a footnote[^1], because no definition exists.
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This is a footnote[^1].
|
||||
/// ///
|
||||
/// /// [^1]: defined here
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub DOC_SUSPICIOUS_FOOTNOTES,
|
||||
suspicious,
|
||||
"looks like a link or footnote ref, but with no definition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects documentation that is empty.
|
||||
/// ### Why is this bad?
|
||||
/// Empty docs clutter code without adding value, reducing readability and maintainability.
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// ///
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub EMPTY_DOCS,
|
||||
suspicious,
|
||||
"docstrings exist but documentation is empty"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -211,6 +493,41 @@
|
||||
"`pub fn` may panic without `# Panics` in doc comment"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// unsafe functions and warns if there is no `# Safety` section.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unsafe functions should document their safety
|
||||
/// preconditions, so that users can be sure they are using them safely.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// At least write a line about safety:
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.39.0"]
|
||||
pub MISSING_SAFETY_DOC,
|
||||
style,
|
||||
"`pub unsafe fn` without `# Safety` docs"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `fn main() { .. }` in doctests
|
||||
@@ -240,126 +557,6 @@
|
||||
"presence of `fn main() {` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[test]` in doctests unless they are marked with
|
||||
/// either `ignore`, `no_run` or `compile_fail`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Code in examples marked as `#[test]` will somewhat
|
||||
/// surprisingly not be run by `cargo test`. If you really want
|
||||
/// to show how to test stuff in an example, mark it `no_run` to
|
||||
/// make the intent clear.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// /// # Examples
|
||||
/// ///
|
||||
/// /// ```
|
||||
/// /// #[test]
|
||||
/// /// fn equality_works() {
|
||||
/// /// assert_eq!(1_u8, 1);
|
||||
/// /// }
|
||||
/// /// ```
|
||||
/// fn test_attr_in_doctest() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub TEST_ATTR_IN_DOCTEST,
|
||||
suspicious,
|
||||
"presence of `#[test]` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the doc comments have unbroken links, mostly caused
|
||||
/// by bad formatted links such as broken across multiple lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Because documentation generated by rustdoc will be broken
|
||||
/// since expected links won't be links and just text.
|
||||
///
|
||||
/// ### Examples
|
||||
/// This link is broken:
|
||||
/// ```no_run
|
||||
/// /// [example of a bad link](https://
|
||||
/// /// github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
///
|
||||
/// It shouldn't be broken across multiple lines to work:
|
||||
/// ```no_run
|
||||
/// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.90.0"]
|
||||
pub DOC_BROKEN_LINK,
|
||||
pedantic,
|
||||
"broken document link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// safe functions and traits and warns if there is a `# Safety` section.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Safe functions and traits are safe to implement and therefore do not
|
||||
/// need to describe safety preconditions that users are required to uphold.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub fn start_apocalypse_but_safely(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The function is safe, so there shouldn't be any preconditions
|
||||
/// that have to be explained for safety reasons.
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub UNNECESSARY_SAFETY_DOC,
|
||||
restriction,
|
||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
|
||||
@@ -409,116 +606,35 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects documentation that is empty.
|
||||
/// Checks for `#[test]` in doctests unless they are marked with
|
||||
/// either `ignore`, `no_run` or `compile_fail`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Empty docs clutter code without adding value, reducing readability and maintainability.
|
||||
/// ### Example
|
||||
/// Code in examples marked as `#[test]` will somewhat
|
||||
/// surprisingly not be run by `cargo test`. If you really want
|
||||
/// to show how to test stuff in an example, mark it `no_run` to
|
||||
/// make the intent clear.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// /// # Examples
|
||||
/// ///
|
||||
/// /// ```
|
||||
/// /// #[test]
|
||||
/// /// fn equality_works() {
|
||||
/// /// assert_eq!(1_u8, 1);
|
||||
/// /// }
|
||||
/// /// ```
|
||||
/// fn test_attr_in_doctest() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub EMPTY_DOCS,
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub TEST_ATTR_IN_DOCTEST,
|
||||
suspicious,
|
||||
"docstrings exist but documentation is empty"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// In CommonMark Markdown, the language used to write doc comments, a
|
||||
/// paragraph nested within a list or block quote does not need any line
|
||||
/// after the first one to be indented or marked. The specification calls
|
||||
/// this a "lazy paragraph continuation."
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This is easy to write but hard to read. Lazy continuations makes
|
||||
/// unintended markers hard to see, and make it harder to deduce the
|
||||
/// document's intended structure.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// This table is probably intended to have two rows,
|
||||
/// but it does not. It has zero rows, and is followed by
|
||||
/// a block quote.
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// >= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by escaping the marker:
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// \>= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// This example is actually intended to be a list:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by indenting the list contents:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub DOC_LAZY_CONTINUATION,
|
||||
style,
|
||||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Detects overindented list items in doc comments where the continuation
|
||||
/// lines are indented more than necessary.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Overindented list items in doc comments can lead to inconsistent and
|
||||
/// poorly formatted documentation when rendered. Excessive indentation may
|
||||
/// cause the text to be misinterpreted as a nested list item or code block,
|
||||
/// affecting readability and the overall structure of the documentation.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fixes this into:
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"ensure list items are not overindented"
|
||||
"presence of `#[test]` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -554,156 +670,63 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if included files in doc comments are included only for `cfg(doc)`.
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// safe functions and traits and warns if there is a `# Safety` section.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// These files are not useful for compilation but will still be included.
|
||||
/// Also, if any of these non-source code file is updated, it will trigger a
|
||||
/// recompilation.
|
||||
/// Safe functions and traits are safe to implement and therefore do not
|
||||
/// need to describe safety preconditions that users are required to uphold.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// Excluding this will currently result in the file being left out if
|
||||
/// the item's docs are inlined from another crate. This may be fixed in a
|
||||
/// future version of rustdoc.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #![doc = include_str!("some_file.md")]
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_INCLUDE_WITHOUT_CFG,
|
||||
restriction,
|
||||
"check if files included in documentation are behind `cfg(doc)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if a link reference definition appears at the start of a
|
||||
/// list item or quote.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably intended as an intra-doc link. If it is really
|
||||
/// supposed to be a reference definition, it can be written outside
|
||||
/// of the list item or quote.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! - [link]: description
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! - [link][]: description (for intra-doc link)
|
||||
/// //!
|
||||
/// //! [link]: destination (for link reference definition)
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_NESTED_REFDEFS,
|
||||
suspicious,
|
||||
"link reference defined in list item or quote"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
|
||||
/// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
|
||||
/// is clearer in this regard.
|
||||
///
|
||||
/// ### Example
|
||||
/// The two replacement dots in this example represent a double space.
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and··
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub fn start_apocalypse_but_safely(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// The function is safe, so there shouldn't be any preconditions
|
||||
/// that have to be explained for safety reasons.
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and\
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
pedantic,
|
||||
"double-space used for doc comment linebreak instead of `\\`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects syntax that looks like a footnote reference.
|
||||
///
|
||||
/// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
|
||||
/// GFM does not parse a footnote reference unless its definition also
|
||||
/// exists. This lint checks for footnote references with missing
|
||||
/// definitions, unless it thinks you're writing a regex.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This probably means that a footnote was meant to exist,
|
||||
/// but was not written.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// This is not a footnote[^1], because no definition exists.
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This is a footnote[^1].
|
||||
/// ///
|
||||
/// /// [^1]: defined here
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub DOC_SUSPICIOUS_FOOTNOTES,
|
||||
suspicious,
|
||||
"looks like a link or footnote ref, but with no definition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.
|
||||
/// Various Markdowns constructs are taken into account to avoid false positives.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A project may wish to enforce consistent doc comments by making sure paragraphs end with a
|
||||
/// punctuation mark.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Returns a random number
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// Returns a random number.
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll.
|
||||
/// ```
|
||||
///
|
||||
/// ### Terminal punctuation marks
|
||||
/// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.
|
||||
///
|
||||
/// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
|
||||
/// introduce a table or a list for example.
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub UNNECESSARY_SAFETY_DOC,
|
||||
restriction,
|
||||
"missing terminal punctuation in doc comments"
|
||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_BROKEN_LINK,
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_LINK_CODE,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
DOC_NESTED_REFDEFS,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
EMPTY_DOCS,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
MISSING_SAFETY_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
TEST_ATTR_IN_DOCTEST,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
]);
|
||||
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
check_private_items: bool,
|
||||
@@ -718,29 +741,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_LINK_CODE,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_BROKEN_LINK,
|
||||
DOC_MARKDOWN,
|
||||
DOC_NESTED_REFDEFS,
|
||||
MISSING_SAFETY_DOC,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN,
|
||||
TEST_ATTR_IN_DOCTEST,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for Documentation {
|
||||
fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
|
||||
include_in_doc_without_cfg::check(cx, attrs);
|
||||
|
||||
@@ -70,17 +70,13 @@
|
||||
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DropForgetRef => [DROP_NON_DROP, FORGET_NON_DROP, MEM_FORGET]);
|
||||
|
||||
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
|
||||
Dropping such a type only extends its contained lifetimes";
|
||||
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
|
||||
Forgetting such a type is the same as dropping it";
|
||||
|
||||
declare_lint_pass!(DropForgetRef => [
|
||||
DROP_NON_DROP,
|
||||
FORGET_NON_DROP,
|
||||
MEM_FORGET,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Call(path, [arg]) = expr.kind
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
"file loaded as module multiple times"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
|
||||
|
||||
struct Modules {
|
||||
local_path: PathBuf,
|
||||
spans: Vec<Span>,
|
||||
@@ -59,8 +61,6 @@ pub struct DuplicateMod {
|
||||
modules: BTreeMap<PathBuf, Modules>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
|
||||
|
||||
impl EarlyLintPass for DuplicateMod {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No { .. }, mod_spans)) = &item.kind
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
restriction,
|
||||
"empty `Drop` implementations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyDrop {
|
||||
|
||||
@@ -12,41 +12,6 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after doc comments.
|
||||
@@ -88,6 +53,46 @@
|
||||
"empty line after doc comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyLineAfter => [
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
]);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemInfo {
|
||||
kind: &'static str,
|
||||
@@ -100,11 +105,6 @@ pub struct EmptyLineAfter {
|
||||
items: Vec<ItemInfo>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyLineAfter => [
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
]);
|
||||
|
||||
impl EmptyLineAfter {
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
|
||||
@@ -14,34 +14,6 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Empty brackets after a struct declaration can be omitted,
|
||||
/// and it may be desirable to do so consistently for style.
|
||||
///
|
||||
/// However, removing the brackets also introduces a public constant named after the struct,
|
||||
/// so this is not just a syntactic simplification but an API change, and adding them back
|
||||
/// is a *breaking* API change.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct Cookie {}
|
||||
/// struct Biscuit();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct Cookie;
|
||||
/// struct Biscuit;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
restriction,
|
||||
"finds struct declarations with empty brackets"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds enum variants without fields that are declared with empty brackets.
|
||||
@@ -77,6 +49,39 @@
|
||||
"finds enum variants with empty brackets"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Empty brackets after a struct declaration can be omitted,
|
||||
/// and it may be desirable to do so consistently for style.
|
||||
///
|
||||
/// However, removing the brackets also introduces a public constant named after the struct,
|
||||
/// so this is not just a syntactic simplification but an API change, and adding them back
|
||||
/// is a *breaking* API change.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct Cookie {}
|
||||
/// struct Biscuit();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct Cookie;
|
||||
/// struct Biscuit;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
restriction,
|
||||
"finds struct declarations with empty brackets"
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyWithBrackets => [
|
||||
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||
EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
]);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Usage {
|
||||
Unused { redundant_use_sites: Vec<Span> },
|
||||
@@ -90,8 +95,6 @@ pub struct EmptyWithBrackets {
|
||||
empty_tuple_enum_variants: FxIndexMap<LocalDefId, Usage>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyWithBrackets {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// FIXME: handle `struct $name {}`
|
||||
|
||||
@@ -8,6 +8,24 @@
|
||||
use rustc_span::Symbol;
|
||||
use std::fmt::Write;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To ensure use of little-endian or the target’s endianness rather than big-endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_be_bytes();
|
||||
/// let _y = 2i64.to_be_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub BIG_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_be_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
|
||||
@@ -45,25 +63,11 @@
|
||||
"disallows usage of the `to_le_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To ensure use of little-endian or the target’s endianness rather than big-endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_be_bytes();
|
||||
/// let _y = 2i64.to_be_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub BIG_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_be_bytes` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
|
||||
declare_lint_pass!(EndianBytes => [
|
||||
BIG_ENDIAN_BYTES,
|
||||
HOST_ENDIAN_BYTES,
|
||||
LITTLE_ENDIAN_BYTES,
|
||||
]);
|
||||
|
||||
const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes];
|
||||
const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes];
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr, is_expr_used_or_unified, paths, peel_hir_expr_while, span_contains_non_whitespace, sym};
|
||||
use clippy_utils::{
|
||||
SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr,
|
||||
is_expr_used_or_unified, paths, peel_hir_expr_while, span_contains_non_whitespace, sym,
|
||||
};
|
||||
use core::fmt::{self, Write};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
restriction,
|
||||
"exported types named `Error` that implement `Error`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
||||
|
||||
@@ -50,6 +50,8 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
"using `Box<T>` where unnecessary"
|
||||
}
|
||||
|
||||
impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
|
||||
|
||||
fn is_non_trait_box(ty: Ty<'_>) -> bool {
|
||||
ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait())
|
||||
}
|
||||
@@ -61,8 +63,6 @@ struct EscapeDelegate<'a, 'tcx> {
|
||||
too_large_for_stack: u64,
|
||||
}
|
||||
|
||||
impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -65,7 +65,10 @@
|
||||
"redundant closures for method calls"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
|
||||
declare_lint_pass!(EtaReduction => [
|
||||
REDUNDANT_CLOSURE,
|
||||
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
@@ -215,6 +218,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
||||
"redundant closure",
|
||||
|diag| {
|
||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
let n_refs = callee_ty_adjustments
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0, |acc, adjustment| match adjustment.kind {
|
||||
Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1,
|
||||
Adjust::Deref(_) if acc > 0 => acc + 1,
|
||||
_ => acc,
|
||||
});
|
||||
if n_refs > 0 {
|
||||
snippet = format!("{}{snippet}", "*".repeat(n_refs));
|
||||
}
|
||||
|
||||
if callee.res_local_id().is_some_and(|l| {
|
||||
// FIXME: Do we really need this `local_used_in` check?
|
||||
// Isn't it checking something like... `callee(callee)`?
|
||||
@@ -230,18 +245,6 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else if let n_refs =
|
||||
callee_ty_adjustments
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0, |acc, adjustment| match adjustment.kind {
|
||||
Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1,
|
||||
Adjust::Deref(_) if acc > 0 => acc + 1,
|
||||
_ => acc,
|
||||
})
|
||||
&& n_refs > 0
|
||||
{
|
||||
snippet = format!("{}{snippet}", "*".repeat(n_refs));
|
||||
}
|
||||
|
||||
let replace_with = match callee_ty_adjusted.kind() {
|
||||
|
||||
@@ -9,6 +9,44 @@
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive use of
|
||||
/// bools in function definitions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calls to such functions
|
||||
/// are confusing and error prone, because it's
|
||||
/// hard to remember argument order and you have
|
||||
/// no type system support to back you up. Using
|
||||
/// two-variant enums instead of bools often makes
|
||||
/// API easier to use.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// fn f(is_round: bool, is_hot: bool) { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// enum Shape {
|
||||
/// Round,
|
||||
/// Spiky,
|
||||
/// }
|
||||
///
|
||||
/// enum Temperature {
|
||||
/// Hot,
|
||||
/// IceCold,
|
||||
/// }
|
||||
///
|
||||
/// fn f(shape: Shape, temperature: Temperature) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
pedantic,
|
||||
"using too many bools in function parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive
|
||||
@@ -50,43 +88,10 @@
|
||||
"using too many bools in a struct"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive use of
|
||||
/// bools in function definitions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calls to such functions
|
||||
/// are confusing and error prone, because it's
|
||||
/// hard to remember argument order and you have
|
||||
/// no type system support to back you up. Using
|
||||
/// two-variant enums instead of bools often makes
|
||||
/// API easier to use.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// fn f(is_round: bool, is_hot: bool) { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// enum Shape {
|
||||
/// Round,
|
||||
/// Spiky,
|
||||
/// }
|
||||
///
|
||||
/// enum Temperature {
|
||||
/// Hot,
|
||||
/// IceCold,
|
||||
/// }
|
||||
///
|
||||
/// fn f(shape: Shape, temperature: Temperature) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
pedantic,
|
||||
"using too many bools in function parameters"
|
||||
}
|
||||
impl_lint_pass!(ExcessiveBools => [
|
||||
FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
STRUCT_EXCESSIVE_BOOLS,
|
||||
]);
|
||||
|
||||
pub struct ExcessiveBools {
|
||||
max_struct_bools: u64,
|
||||
@@ -102,8 +107,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
|
||||
|
||||
fn has_n_bools<'tcx>(iter: impl Iterator<Item = &'tcx Ty<'tcx>>, mut count: u64) -> bool {
|
||||
iter.filter(|ty| is_bool(ty)).any(|_| {
|
||||
let (x, overflow) = count.overflowing_sub(1);
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
complexity,
|
||||
"checks for blocks nested beyond a certain threshold"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
|
||||
|
||||
pub struct ExcessiveNesting {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
||||
|
||||
pub struct ExplicitWrite {
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
@@ -49,8 +51,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// match call to unwrap
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"unused type parameters in function definitions"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
|
||||
|
||||
pub struct ExtraUnusedTypeParameters {
|
||||
avoid_breaking_exported_api: bool,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
|
||||
|
||||
/// A visitor struct that walks a given function and gathers generic type parameters, plus any
|
||||
/// trait bounds those parameters have.
|
||||
struct TypeWalker<'cx, 'tcx> {
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
"checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MODIFIERS]);
|
||||
declare_lint_pass!(FieldScopedVisibilityModifiers => [
|
||||
FIELD_SCOPED_VISIBILITY_MODIFIERS,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for FieldScopedVisibilityModifiers {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
|
||||
@@ -61,14 +61,12 @@
|
||||
"lossy whole number float literals"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
|
||||
|
||||
pub struct FloatLiteral {
|
||||
const_literal_digits_threshold: usize,
|
||||
}
|
||||
|
||||
impl_lint_pass!(FloatLiteral => [
|
||||
EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL
|
||||
]);
|
||||
|
||||
impl FloatLiteral {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -103,10 +103,7 @@
|
||||
"usage of sub-optimal floating point operations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FloatingPointArithmetic => [
|
||||
IMPRECISE_FLOPS,
|
||||
SUBOPTIMAL_FLOPS
|
||||
]);
|
||||
declare_lint_pass!(FloatingPointArithmetic => [IMPRECISE_FLOPS, SUBOPTIMAL_FLOPS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, has_ambiguous_literal_in_expr, sym};
|
||||
use rustc_ast::AssignOpKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
@@ -26,69 +27,99 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
let (is_assign, op, lhs, rhs) = match &expr.kind {
|
||||
ExprKind::AssignOp(
|
||||
Spanned {
|
||||
node: AssignOpKind::AddAssign,
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) => (true, BinOpKind::Add, lhs, rhs),
|
||||
ExprKind::AssignOp(
|
||||
Spanned {
|
||||
node: AssignOpKind::SubAssign,
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) => (true, BinOpKind::Sub, lhs, rhs),
|
||||
ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) => (false, *op, lhs, rhs),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if !is_assign
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
|
||||
&& method.name == sym::sqrt
|
||||
// we don't care about the applicability as this is an early-return condition
|
||||
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
|
||||
&& method.name == sym::sqrt
|
||||
// we don't care about the applicability as this is an early-return condition
|
||||
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_neg_sugg = |expr, app: &mut _| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
|
||||
if let BinOpKind::Sub = op { -sugg } else { sugg }
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
(
|
||||
inner_lhs,
|
||||
Sugg::hir_with_applicability(cx, inner_rhs, "_", &mut app),
|
||||
maybe_neg_sugg(rhs, &mut app),
|
||||
)
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
{
|
||||
(
|
||||
inner_lhs,
|
||||
maybe_neg_sugg(inner_rhs, &mut app),
|
||||
Sugg::hir_with_applicability(cx, lhs, "_", &mut app),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
|
||||
// see: https://github.com/rust-lang/rust-clippy/issues/14897
|
||||
if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
|
||||
&& has_ambiguous_literal_in_expr(cx, recv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({arg1}, {arg2})",
|
||||
super::lib::prepare_receiver_sugg(cx, recv, &mut app)
|
||||
),
|
||||
app,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
|
||||
// see: https://github.com/rust-lang/rust-clippy/issues/14897
|
||||
let has_ambiguous_type = |expr: &Expr<'_>| {
|
||||
(matches!(expr.kind, ExprKind::Path(_)) || matches!(expr.kind, ExprKind::Call(_, _)))
|
||||
&& has_ambiguous_literal_in_expr(cx, expr)
|
||||
};
|
||||
|
||||
let (recv, arg1, arg2, is_from_rhs) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& !has_ambiguous_type(inner_lhs)
|
||||
{
|
||||
(inner_lhs, inner_rhs, lhs, true)
|
||||
} else if !is_assign
|
||||
&& let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
&& !has_ambiguous_type(inner_lhs)
|
||||
{
|
||||
(inner_lhs, inner_rhs, rhs, false)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
|diag| {
|
||||
let maybe_neg_sugg = |expr, app: &mut _| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
|
||||
if let BinOpKind::Sub = op { -sugg } else { sugg }
|
||||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv_sugg = super::lib::prepare_receiver_sugg(cx, recv, &mut app);
|
||||
let (arg1, arg2) = if is_from_rhs {
|
||||
(
|
||||
maybe_neg_sugg(arg1, &mut app),
|
||||
Sugg::hir_with_applicability(cx, arg2, "_", &mut app),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Sugg::hir_with_applicability(cx, arg1, "_", &mut app),
|
||||
maybe_neg_sugg(arg2, &mut app),
|
||||
)
|
||||
};
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider using",
|
||||
if is_assign {
|
||||
format!("{arg2} = {recv_sugg}.mul_add({arg1}, {arg2})")
|
||||
} else {
|
||||
format!("{recv_sugg}.mul_add({arg1}, {arg2})")
|
||||
},
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"useless use of `format!`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||
|
||||
pub struct UselessFormat {
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
@@ -49,8 +51,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
|
||||
@@ -58,32 +58,30 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`.
|
||||
/// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
|
||||
/// or any types that have a derived `Debug` impl that recursively contains them.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust doesn't guarantee what `Debug` formatting looks like, and it could
|
||||
/// change in the future. `OsStr`s and `Path`s can be `Display` formatted
|
||||
/// using their `display` methods.
|
||||
/// ### Why restrict this?
|
||||
/// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
|
||||
/// certain data structures or functions from prying hacker eyes as an additional line of security.
|
||||
///
|
||||
/// Furthermore, with `Debug` formatting, certain characters are escaped.
|
||||
/// Thus, a `Debug` formatted `Path` is less likely to be clickable.
|
||||
/// ### Known problems
|
||||
/// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
|
||||
/// implementation prints an address is left as an exercise to the next lint implementer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("...");
|
||||
/// println!("The path is {:?}", path);
|
||||
/// let foo = &0_u32;
|
||||
/// fn bar() {}
|
||||
/// println!("{:p}", foo);
|
||||
/// let _ = format!("{:?}", &(bar as fn()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("…");
|
||||
/// println!("The path is {}", path.display());
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub UNNECESSARY_DEBUG_FORMATTING,
|
||||
pedantic,
|
||||
"`Debug` formatting applied to an `OsStr` or `Path` when `.display()` is available"
|
||||
///
|
||||
/// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub POINTER_FORMAT,
|
||||
restriction,
|
||||
"formatting a pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -171,61 +169,32 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [formatting parameters] that have no effect on the output of
|
||||
/// `format!()`, `println!()` or similar macros.
|
||||
/// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Shorter format specifiers are easier to read, it may also indicate that
|
||||
/// an expected formatting operation such as adding padding isn't happening.
|
||||
/// Rust doesn't guarantee what `Debug` formatting looks like, and it could
|
||||
/// change in the future. `OsStr`s and `Path`s can be `Display` formatted
|
||||
/// using their `display` methods.
|
||||
///
|
||||
/// Furthermore, with `Debug` formatting, certain characters are escaped.
|
||||
/// Thus, a `Debug` formatted `Path` is less likely to be clickable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{:.}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {:5}", format_args!("..."));
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("...");
|
||||
/// println!("The path is {:?}", path);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!("{}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {}", format_args!("..."));
|
||||
/// // OR
|
||||
/// println!("padded: {:5}", format!("..."));
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("…");
|
||||
/// println!("The path is {}", path.display());
|
||||
/// ```
|
||||
///
|
||||
/// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNUSED_FORMAT_SPECS,
|
||||
complexity,
|
||||
"use of a format specifier that has no effect"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
|
||||
/// or any types that have a derived `Debug` impl that recursively contains them.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
|
||||
/// certain data structures or functions from prying hacker eyes as an additional line of security.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
|
||||
/// implementation prints an address is left as an exercise to the next lint implementer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let foo = &0_u32;
|
||||
/// fn bar() {}
|
||||
/// println!("{:p}", foo);
|
||||
/// let _ = format!("{:?}", &(bar as fn()));
|
||||
/// ```
|
||||
///
|
||||
/// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub POINTER_FORMAT,
|
||||
restriction,
|
||||
"formatting a pointer"
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub UNNECESSARY_DEBUG_FORMATTING,
|
||||
pedantic,
|
||||
"`Debug` formatting applied to an `OsStr` or `Path` when `.display()` is available"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -257,14 +226,45 @@
|
||||
"unnecessary trailing comma before closing parenthesis"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [formatting parameters] that have no effect on the output of
|
||||
/// `format!()`, `println!()` or similar macros.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Shorter format specifiers are easier to read, it may also indicate that
|
||||
/// an expected formatting operation such as adding padding isn't happening.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{:.}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {:5}", format_args!("..."));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!("{}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {}", format_args!("..."));
|
||||
/// // OR
|
||||
/// println!("padded: {:5}", format!("..."));
|
||||
/// ```
|
||||
///
|
||||
/// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNUSED_FORMAT_SPECS,
|
||||
complexity,
|
||||
"use of a format specifier that has no effect"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatArgs<'_> => [
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
POINTER_FORMAT,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
UNINLINED_FORMAT_ARGS,
|
||||
UNNECESSARY_DEBUG_FORMATTING,
|
||||
UNUSED_FORMAT_SPECS,
|
||||
POINTER_FORMAT,
|
||||
UNNECESSARY_TRAILING_COMMA,
|
||||
UNUSED_FORMAT_SPECS,
|
||||
]);
|
||||
|
||||
#[expect(clippy::struct_field_names)]
|
||||
|
||||
@@ -10,45 +10,6 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
|
||||
/// which uses `self` as a parameter.
|
||||
/// This is typically done indirectly with the `write!` macro or with `to_string()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will lead to infinite recursion and a stack overflow.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.to_string())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.0)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub RECURSIVE_FORMAT_IMPL,
|
||||
correctness,
|
||||
"Format trait method called while implementing the same Format trait"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `println`, `print`, `eprintln` or `eprint` in an
|
||||
@@ -90,6 +51,47 @@
|
||||
"use of a print macro in a formatting trait impl"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
|
||||
/// which uses `self` as a parameter.
|
||||
/// This is typically done indirectly with the `write!` macro or with `to_string()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will lead to infinite recursion and a stack overflow.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.to_string())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.0)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub RECURSIVE_FORMAT_IMPL,
|
||||
correctness,
|
||||
"Format trait method called while implementing the same Format trait"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatImpl => [PRINT_IN_FORMAT_IMPL, RECURSIVE_FORMAT_IMPL]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct FormatTraitNames {
|
||||
/// e.g. `sym::Display`
|
||||
@@ -113,8 +115,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatImpl => [RECURSIVE_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
||||
self.format_trait_impl = is_format_trait_impl(cx, impl_item);
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
pedantic,
|
||||
"`format!(..)` appended to existing `String`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
|
||||
|
||||
pub(crate) struct FormatPushString {
|
||||
|
||||
@@ -6,6 +6,52 @@
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This could lead to unexpected results.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub POSSIBLE_MISSING_COMMA,
|
||||
correctness,
|
||||
"possible missing comma in array"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for an `if` expression followed by either a block or another `if` that
|
||||
/// looks like it should have an `else` between them.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably some refactoring remnant, even if the code is correct, it
|
||||
/// might look confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.91.0"]
|
||||
pub POSSIBLE_MISSING_ELSE,
|
||||
suspicious,
|
||||
"possibly missing `else`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the non-existent `=*`, `=!` and `=-`
|
||||
@@ -25,35 +71,6 @@
|
||||
"suspicious formatting of `*=`, `-=` or `!=`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the formatting of a unary operator on the right hand side
|
||||
/// of a binary operator. It lints if there is no space between the binary and unary operators,
|
||||
/// but there is a space between the unary and its operand.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is either a typo in the binary operator or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// // &&! looks like a different operator
|
||||
/// if foo &&! bar {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// if foo && !bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
suspicious,
|
||||
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for formatting of `else`. It lints if the `else`
|
||||
@@ -93,56 +110,39 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for an `if` expression followed by either a block or another `if` that
|
||||
/// looks like it should have an `else` between them.
|
||||
/// Checks the formatting of a unary operator on the right hand side
|
||||
/// of a binary operator. It lints if there is no space between the binary and unary operators,
|
||||
/// but there is a space between the unary and its operand.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably some refactoring remnant, even if the code is correct, it
|
||||
/// might look confusing.
|
||||
/// This is either a typo in the binary operator or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// // &&! looks like a different operator
|
||||
/// if foo &&! bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.91.0"]
|
||||
pub POSSIBLE_MISSING_ELSE,
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// if foo && !bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
suspicious,
|
||||
"possibly missing `else`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This could lead to unexpected results.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub POSSIBLE_MISSING_COMMA,
|
||||
correctness,
|
||||
"possible missing comma in array"
|
||||
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Formatting => [
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
POSSIBLE_MISSING_COMMA,
|
||||
POSSIBLE_MISSING_ELSE,
|
||||
POSSIBLE_MISSING_COMMA
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for Formatting {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
suspicious,
|
||||
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"Warns on implementations of `Into<..>` to use `From<..>`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
pub struct FromOverInto {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -62,8 +64,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl(Impl {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
suspicious,
|
||||
"creating a `Box` from a void raw pointer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
|
||||
|
||||
impl LateLintPass<'_> for FromRawWithVoidPtr {
|
||||
|
||||
+276
-276
@@ -21,6 +21,30 @@
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute without
|
||||
/// further information on functions and methods that return a type already
|
||||
/// marked as `#[must_use]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute isn't needed. Not using the result
|
||||
/// will already be reported. Alternatively, one can add some text to the
|
||||
/// attribute to improve the lint message.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn double_must_use() -> Result<(), ()> {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub DOUBLE_MUST_USE,
|
||||
style,
|
||||
"`#[must_use]` attribute on a `#[must_use]`-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for function arguments having the similar names
|
||||
@@ -46,48 +70,122 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions with too many parameters.
|
||||
/// Lints when `impl Trait` is being used in a function's parameters.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
/// ### Why restrict this?
|
||||
/// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
|
||||
/// making `impl Trait` less powerful. Readability may also be a factor.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # struct Color;
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// // ..
|
||||
/// trait MyTrait {}
|
||||
/// fn foo(a: impl MyTrait) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
complexity,
|
||||
"functions with too many arguments"
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo<T: MyTrait>(a: T) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub IMPL_TRAIT_IN_PARAMS,
|
||||
restriction,
|
||||
"`impl Trait` is used in the function's parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions with a large amount of lines.
|
||||
/// Checks for getter methods that return a field that doesn't correspond
|
||||
/// to the name of the method, when there is a field's whose name matches that of the method.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
/// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
|
||||
///
|
||||
/// ### Example
|
||||
|
||||
/// ```no_run
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.b
|
||||
/// }
|
||||
/// }
|
||||
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.a
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.34.0"]
|
||||
pub TOO_MANY_LINES,
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub MISNAMED_GETTERS,
|
||||
suspicious,
|
||||
"getter method returning the wrong field"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for public functions that have no
|
||||
/// `#[must_use]` attribute, but return something not already marked
|
||||
/// must-use, have no mutable arg and mutate no statics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not bad at all, this lint just shows places where
|
||||
/// you could add the attribute.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint only checks the arguments for mutable
|
||||
/// types without looking if they are actually changed. On the other hand,
|
||||
/// it also ignores a broad range of potentially interesting side effects,
|
||||
/// because we cannot decide whether the programmer intends the function to
|
||||
/// be called for the side effect or the result. Expect many false
|
||||
/// positives. At least we don't lint if the result type is unit or already
|
||||
/// `#[must_use]`.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// // this could be annotated with `#[must_use]`.
|
||||
/// pub fn id<T>(t: T) -> T { t }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_CANDIDATE,
|
||||
pedantic,
|
||||
"functions with too many lines"
|
||||
"function or method that could take a `#[must_use]` attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute on
|
||||
/// unit-returning functions and methods.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unit values are useless. The attribute is likely
|
||||
/// a remnant of a refactoring that removed the return type.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn useless() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_UNIT,
|
||||
style,
|
||||
"`#[must_use]` attribute on a unit-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -152,76 +250,131 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute on
|
||||
/// unit-returning functions and methods.
|
||||
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unit values are useless. The attribute is likely
|
||||
/// a remnant of a refactoring that removed the return type.
|
||||
/// More flexibility, better memory optimization, and more idiomatic Rust code.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn useless() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_UNIT,
|
||||
style,
|
||||
"`#[must_use]` attribute on a unit-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute without
|
||||
/// further information on functions and methods that return a type already
|
||||
/// marked as `#[must_use]`.
|
||||
/// `&Option<T>` in a function signature breaks encapsulation because the caller must own T
|
||||
/// and move it into an Option to call with it. When returned, the owner must internally store
|
||||
/// it as `Option<T>` in order to return it.
|
||||
/// At a lower level, `&Option<T>` points to memory with the `presence` bit flag plus the `T` value,
|
||||
/// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation)
|
||||
/// to a single pointer, so it may be more optimal.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute isn't needed. Not using the result
|
||||
/// will already be reported. Alternatively, one can add some text to the
|
||||
/// attribute to improve the lint message.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn double_must_use() -> Result<(), ()> {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub DOUBLE_MUST_USE,
|
||||
style,
|
||||
"`#[must_use]` attribute on a `#[must_use]`-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for public functions that have no
|
||||
/// `#[must_use]` attribute, but return something not already marked
|
||||
/// must-use, have no mutable arg and mutate no statics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not bad at all, this lint just shows places where
|
||||
/// you could add the attribute.
|
||||
/// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by
|
||||
/// Logan Smith for an in-depth explanation of why this is important.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint only checks the arguments for mutable
|
||||
/// types without looking if they are actually changed. On the other hand,
|
||||
/// it also ignores a broad range of potentially interesting side effects,
|
||||
/// because we cannot decide whether the programmer intends the function to
|
||||
/// be called for the side effect or the result. Expect many false
|
||||
/// positives. At least we don't lint if the result type is unit or already
|
||||
/// `#[must_use]`.
|
||||
/// This lint recommends changing the function signatures, but it cannot
|
||||
/// automatically change the function calls or the function implementations.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // caller uses foo(&opt)
|
||||
/// fn foo(a: &Option<String>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> &Option<String> { &None }
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // caller should use `foo1(opt.as_ref())`
|
||||
/// fn foo1(a: Option<&String>) {}
|
||||
/// // better yet, use string slice `foo2(opt.as_deref())`
|
||||
/// fn foo2(a: Option<&str>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> Option<&String> { None }
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub REF_OPTION,
|
||||
pedantic,
|
||||
"function signature uses `&Option<T>` instead of `Option<&T>`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when the name of function parameters from trait impl is
|
||||
/// different than its default implementation.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Using the default name for parameters of a trait method is more consistent.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, b: &Self) -> bool {
|
||||
/// self.0 == b.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// self.0 == other.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub RENAMED_FUNCTION_PARAMS,
|
||||
restriction,
|
||||
"renamed function parameters in trait implementation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that return `Result` with an unusually large
|
||||
/// `Err`-variant.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldom used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
/// `Err`-variant further up the call-stack stores the `Err`-variant in
|
||||
/// question (as library code often does), it itself needs to be at least
|
||||
/// as large, propagating the problem.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// // this could be annotated with `#[must_use]`.
|
||||
/// pub fn id<T>(t: T) -> T { t }
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes([u8; 512]),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_CANDIDATE,
|
||||
pedantic,
|
||||
"function or method that could take a `#[must_use]` attribute"
|
||||
/// should be
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes(Box<[u8; 512]>),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` is slightly larger than a pointer
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub RESULT_LARGE_ERR,
|
||||
perf,
|
||||
"function returning `Result` with large `Err` type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -276,205 +429,67 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that return `Result` with an unusually large
|
||||
/// `Err`-variant.
|
||||
/// Checks for functions with too many parameters.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldom used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
/// `Err`-variant further up the call-stack stores the `Err`-variant in
|
||||
/// question (as library code often does), it itself needs to be at least
|
||||
/// as large, propagating the problem.
|
||||
/// Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes([u8; 512]),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// # struct Color;
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// should be
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes(Box<[u8; 512]>),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` is slightly larger than a pointer
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub RESULT_LARGE_ERR,
|
||||
perf,
|
||||
"function returning `Result` with large `Err` type"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
complexity,
|
||||
"functions with too many arguments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getter methods that return a field that doesn't correspond
|
||||
/// to the name of the method, when there is a field's whose name matches that of the method.
|
||||
/// Checks for functions with a large amount of lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
|
||||
///
|
||||
/// ### Example
|
||||
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.b
|
||||
/// }
|
||||
/// }
|
||||
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.a
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub MISNAMED_GETTERS,
|
||||
suspicious,
|
||||
"getter method returning the wrong field"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when `impl Trait` is being used in a function's parameters.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
|
||||
/// making `impl Trait` less powerful. Readability may also be a factor.
|
||||
/// Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo(a: impl MyTrait) {
|
||||
/// // [...]
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo<T: MyTrait>(a: T) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub IMPL_TRAIT_IN_PARAMS,
|
||||
restriction,
|
||||
"`impl Trait` is used in the function's parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when the name of function parameters from trait impl is
|
||||
/// different than its default implementation.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Using the default name for parameters of a trait method is more consistent.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, b: &Self) -> bool {
|
||||
/// self.0 == b.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// self.0 == other.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub RENAMED_FUNCTION_PARAMS,
|
||||
restriction,
|
||||
"renamed function parameters in trait implementation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// More flexibility, better memory optimization, and more idiomatic Rust code.
|
||||
///
|
||||
/// `&Option<T>` in a function signature breaks encapsulation because the caller must own T
|
||||
/// and move it into an Option to call with it. When returned, the owner must internally store
|
||||
/// it as `Option<T>` in order to return it.
|
||||
/// At a lower level, `&Option<T>` points to memory with the `presence` bit flag plus the `T` value,
|
||||
/// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation)
|
||||
/// to a single pointer, so it may be more optimal.
|
||||
///
|
||||
/// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by
|
||||
/// Logan Smith for an in-depth explanation of why this is important.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint recommends changing the function signatures, but it cannot
|
||||
/// automatically change the function calls or the function implementations.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // caller uses foo(&opt)
|
||||
/// fn foo(a: &Option<String>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> &Option<String> { &None }
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // caller should use `foo1(opt.as_ref())`
|
||||
/// fn foo1(a: Option<&String>) {}
|
||||
/// // better yet, use string slice `foo2(opt.as_deref())`
|
||||
/// fn foo2(a: Option<&str>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> Option<&String> { None }
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub REF_OPTION,
|
||||
#[clippy::version = "1.34.0"]
|
||||
pub TOO_MANY_LINES,
|
||||
pedantic,
|
||||
"function signature uses `&Option<T>` instead of `Option<&T>`"
|
||||
"functions with too many lines"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EarlyFunctions => [DUPLICATE_UNDERSCORE_ARGUMENT]);
|
||||
|
||||
impl_lint_pass!(Functions => [
|
||||
DOUBLE_MUST_USE,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
MISNAMED_GETTERS,
|
||||
MUST_USE_CANDIDATE,
|
||||
MUST_USE_UNIT,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
REF_OPTION,
|
||||
RENAMED_FUNCTION_PARAMS,
|
||||
RESULT_LARGE_ERR,
|
||||
RESULT_UNIT_ERR,
|
||||
TOO_MANY_ARGUMENTS,
|
||||
TOO_MANY_LINES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyFunctions {
|
||||
fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: visit::FnKind<'_>, _: Span, _: ast::NodeId) {
|
||||
duplicate_underscore_argument::check(cx, fn_kind);
|
||||
@@ -515,21 +530,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Functions => [
|
||||
TOO_MANY_ARGUMENTS,
|
||||
TOO_MANY_LINES,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
MUST_USE_UNIT,
|
||||
DOUBLE_MUST_USE,
|
||||
MUST_USE_CANDIDATE,
|
||||
RESULT_UNIT_ERR,
|
||||
RESULT_LARGE_ERR,
|
||||
MISNAMED_GETTERS,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
RENAMED_FUNCTION_PARAMS,
|
||||
REF_OPTION,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -217,7 +217,7 @@ fn check_must_use_candidate<'tcx>(
|
||||
diag.span_suggestion(
|
||||
item_span.shrink_to_lo(),
|
||||
"add the attribute",
|
||||
format!("#[must_use] \n{indent}"),
|
||||
format!("#[must_use]\n{indent}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
if let Some(msg) = match return_ty(cx, item_id).opt_diag_name(cx) {
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
"Finds if-else that could be written using either `bool::then` or `bool::then_some`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
pub struct IfThenSomeElseNone {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -60,8 +62,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(higher::If {
|
||||
|
||||
+62
-62
@@ -11,6 +11,62 @@
|
||||
mod ifs_same_cond;
|
||||
mod same_functions_in_if_cond;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if the `if` and `else` block contain shared code that can be
|
||||
/// moved out of the blocks.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Duplicate code is less maintainable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// println!("Hello World");
|
||||
/// 13
|
||||
/// } else {
|
||||
/// println!("Hello World");
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// println!("Hello World");
|
||||
/// let foo = if … {
|
||||
/// 13
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.53.0"]
|
||||
pub BRANCHES_SHARING_CODE,
|
||||
nursery,
|
||||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably a copy & paste error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
style,
|
||||
"`if` with the same `then` and `else` blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for consecutive `if`s with the same condition.
|
||||
@@ -91,61 +147,12 @@
|
||||
"consecutive `if`s with the same function call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably a copy & paste error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
style,
|
||||
"`if` with the same `then` and `else` blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if the `if` and `else` block contain shared code that can be
|
||||
/// moved out of the blocks.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Duplicate code is less maintainable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// println!("Hello World");
|
||||
/// 13
|
||||
/// } else {
|
||||
/// println!("Hello World");
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// println!("Hello World");
|
||||
/// let foo = if … {
|
||||
/// 13
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.53.0"]
|
||||
pub BRANCHES_SHARING_CODE,
|
||||
nursery,
|
||||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
impl_lint_pass!(CopyAndPaste<'_> => [
|
||||
BRANCHES_SHARING_CODE,
|
||||
IFS_SAME_COND,
|
||||
IF_SAME_THEN_ELSE,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
]);
|
||||
|
||||
pub struct CopyAndPaste<'tcx> {
|
||||
interior_mut: InteriorMut<'tcx>,
|
||||
@@ -159,13 +166,6 @@ pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CopyAndPaste<'_> => [
|
||||
IFS_SAME_COND,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
IF_SAME_THEN_ELSE,
|
||||
BRANCHES_SHARING_CODE
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
pedantic,
|
||||
"suggest replacing `_` by `()` in patterns where appropriate"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
"ensures that the semantics of `Borrow` for `Hash` are satisfied when `Borrow<str>` and `Borrow<[u8]>` are implemented"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplHashWithBorrowStrBytes => [IMPL_HASH_BORROW_WITH_STR_AND_BYTES]);
|
||||
declare_lint_pass!(ImplHashWithBorrowStrBytes => [
|
||||
IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
|
||||
]);
|
||||
|
||||
impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
|
||||
/// We are emitting this lint at the Hash impl of a type that implements all
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
style,
|
||||
"Perform saturating addition instead of implicitly checking max bound of data type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
||||
|
||||
@@ -79,12 +79,15 @@
|
||||
"Check if a variable is smaller than another one and still subtract from it even if smaller"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ImplicitSaturatingSub => [
|
||||
IMPLICIT_SATURATING_SUB,
|
||||
INVERTED_SATURATING_SUB,
|
||||
]);
|
||||
|
||||
pub struct ImplicitSaturatingSub {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]);
|
||||
|
||||
impl ImplicitSaturatingSub {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
complexity,
|
||||
"specifying bounds that are implied by other bounds in `impl Trait` type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
|
||||
|
||||
fn emit_lint(
|
||||
|
||||
@@ -71,6 +71,8 @@
|
||||
"ensures that all items used in the crate are available for the current MSRV"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Availability {
|
||||
FeatureEnabled,
|
||||
@@ -113,8 +115,6 @@ pub struct IncompatibleMsrv {
|
||||
called_path: Option<HirId>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
|
||||
|
||||
impl IncompatibleMsrv {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -64,6 +64,10 @@
|
||||
"the order of the field init is inconsistent with the order in the struct definition"
|
||||
}
|
||||
|
||||
impl_lint_pass!(InconsistentStructConstructor => [
|
||||
INCONSISTENT_STRUCT_CONSTRUCTOR,
|
||||
]);
|
||||
|
||||
pub struct InconsistentStructConstructor {
|
||||
check_inconsistent_struct_field_initializers: bool,
|
||||
}
|
||||
@@ -76,8 +80,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
let ExprKind::Struct(_, fields, _) = expr.kind else {
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
"avoid indexing on slices which could be destructed"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
||||
|
||||
pub struct IndexRefutableSlice {
|
||||
max_suggested_slice: u64,
|
||||
msrv: Msrv,
|
||||
@@ -67,8 +69,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr)
|
||||
|
||||
@@ -9,36 +9,6 @@
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will always panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let x = [1, 2, 3, 4];
|
||||
/// // Index within bounds
|
||||
///
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OUT_OF_BOUNDS_INDEXING,
|
||||
correctness,
|
||||
"out of bounds constant indexing"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of indexing or slicing that may panic at runtime.
|
||||
@@ -92,6 +62,36 @@
|
||||
"indexing/slicing usage"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will always panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let x = [1, 2, 3, 4];
|
||||
/// // Index within bounds
|
||||
///
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OUT_OF_BOUNDS_INDEXING,
|
||||
correctness,
|
||||
"out of bounds constant indexing"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
|
||||
|
||||
pub struct IndexingSlicing {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
suspicious,
|
||||
"TryFrom with infallible Error type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
|
||||
|
||||
@@ -88,7 +88,10 @@
|
||||
"type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
|
||||
declare_lint_pass!(InherentToString => [
|
||||
INHERENT_TO_STRING,
|
||||
INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
|
||||
@@ -45,6 +45,37 @@
|
||||
"enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modules that have the same name as their
|
||||
/// parent module
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A typical beginner mistake is to have `mod foo;` and
|
||||
/// again `mod foo { ..
|
||||
/// }` in `foo.rs`.
|
||||
/// The expectation is that items inside the inner `mod foo { .. }` are then
|
||||
/// available
|
||||
/// through `foo::x`, but they are only available through
|
||||
/// `foo::foo::x`.
|
||||
/// If this is done on purpose, it would be better to choose a more
|
||||
/// representative module name.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULE_INCEPTION,
|
||||
style,
|
||||
"modules that have the same name as their parent module"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects public item names that are prefixed or suffixed by the
|
||||
@@ -89,36 +120,6 @@
|
||||
"type names prefixed/postfixed with their containing module's name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modules that have the same name as their
|
||||
/// parent module
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A typical beginner mistake is to have `mod foo;` and
|
||||
/// again `mod foo { ..
|
||||
/// }` in `foo.rs`.
|
||||
/// The expectation is that items inside the inner `mod foo { .. }` are then
|
||||
/// available
|
||||
/// through `foo::x`, but they are only available through
|
||||
/// `foo::foo::x`.
|
||||
/// If this is done on purpose, it would be better to choose a more
|
||||
/// representative module name.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULE_INCEPTION,
|
||||
style,
|
||||
"modules that have the same name as their parent module"
|
||||
}
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects struct fields that are prefixed or suffixed
|
||||
@@ -155,6 +156,13 @@
|
||||
"structs where all fields share a prefix/postfix or contain the name of the struct"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ItemNameRepetitions => [
|
||||
ENUM_VARIANT_NAMES,
|
||||
MODULE_INCEPTION,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
STRUCT_FIELD_NAMES,
|
||||
]);
|
||||
|
||||
pub struct ItemNameRepetitions {
|
||||
/// The module path the lint pass is in.
|
||||
modules: Vec<ModInfo>,
|
||||
@@ -195,13 +203,6 @@ fn is_allowed_prefix(&self, prefix: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ItemNameRepetitions => [
|
||||
ENUM_VARIANT_NAMES,
|
||||
STRUCT_FIELD_NAMES,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
MODULE_INCEPTION
|
||||
]);
|
||||
|
||||
#[must_use]
|
||||
fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
|
||||
prefixes.iter().all(|p| p == &"" || p == &"_")
|
||||
|
||||
@@ -9,50 +9,6 @@
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
|
||||
/// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint focuses on providing an idiomatic API. Therefore, it will only
|
||||
/// lint on types which are accessible outside of the crate. For internal types,
|
||||
/// the `IntoIterator` trait can be implemented on demand if it is actually needed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// impl<'a> IntoIterator for &MySlice<'a> {
|
||||
/// type Item = &'a u8;
|
||||
/// type IntoIter = std::slice::Iter<'a, u8>;
|
||||
/// fn into_iter(self) -> Self::IntoIter {
|
||||
/// self.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub ITER_WITHOUT_INTO_ITER,
|
||||
pedantic,
|
||||
"implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This is the opposite of the `iter_without_into_iter` lint.
|
||||
@@ -105,7 +61,54 @@
|
||||
"implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
|
||||
/// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint focuses on providing an idiomatic API. Therefore, it will only
|
||||
/// lint on types which are accessible outside of the crate. For internal types,
|
||||
/// the `IntoIterator` trait can be implemented on demand if it is actually needed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// impl<'a> IntoIterator for &MySlice<'a> {
|
||||
/// type Item = &'a u8;
|
||||
/// type IntoIter = std::slice::Iter<'a, u8>;
|
||||
/// fn into_iter(self) -> Self::IntoIter {
|
||||
/// self.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub ITER_WITHOUT_INTO_ITER,
|
||||
pedantic,
|
||||
"implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IterWithoutIntoIter => [
|
||||
INTO_ITER_WITHOUT_ITER,
|
||||
ITER_WITHOUT_INTO_ITER,
|
||||
]);
|
||||
|
||||
/// Checks if a given type is nameable in a trait (impl).
|
||||
/// RPIT is stable, but impl Trait in traits is not (yet), so when we have
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"large non-scalar const array may cause performance overhead"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
|
||||
|
||||
pub struct LargeConstArrays {
|
||||
maximum_allowed_size: u64,
|
||||
}
|
||||
@@ -44,8 +46,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(ident, generics, _, _) = &item.kind
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
"large size difference between variants on an enum"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
pub struct LargeEnumVariant {
|
||||
maximum_size_difference_allowed: u64,
|
||||
}
|
||||
@@ -70,8 +72,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
|
||||
if let ItemKind::Enum(ident, _, ref def) = item.kind
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"large future may lead to unexpected stack overflows"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
|
||||
|
||||
pub struct LargeFuture {
|
||||
future_size_threshold: u64,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeFuture {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"including a large file"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
||||
|
||||
pub struct LargeIncludeFile {
|
||||
max_file_size: u64,
|
||||
}
|
||||
@@ -50,8 +52,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
||||
|
||||
impl LateLintPass<'_> for LargeIncludeFile {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Lit(lit) = &expr.kind
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_from_proc_macro, sym};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_from_proc_macro, sym};
|
||||
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
@@ -29,6 +29,8 @@
|
||||
"allocating large arrays on stack may cause stack overflow"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||
|
||||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u64,
|
||||
prev_vec_macro_callsite: Option<Span>,
|
||||
@@ -61,8 +63,6 @@ fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user