mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Merge commit '7901289135257ca0fbed3a5522526f95b0f5edba' into clippy-subtree-update
This commit is contained in:
parent
249210e8d8
commit
b61fcbee76
9
.github/deploy.sh
vendored
9
.github/deploy.sh
vendored
@ -25,16 +25,15 @@ if [[ $BETA = "true" ]]; then
|
||||
fi
|
||||
|
||||
# Generate version index that is shown as root index page
|
||||
cp util/gh-pages/versions.html out/index.html
|
||||
|
||||
echo "Making the versions.json file"
|
||||
python3 ./util/versions.py out
|
||||
python3 ./util/versions.py ./util/gh-pages/versions.html out
|
||||
|
||||
# Now let's go have some fun with the cloned repo
|
||||
cd out
|
||||
git config user.name "GHA CI"
|
||||
git config user.email "gha@ci.invalid"
|
||||
|
||||
git status
|
||||
|
||||
if [[ -n $TAG_NAME ]]; then
|
||||
# track files, so that the following check works
|
||||
git add --intent-to-add "$TAG_NAME"
|
||||
@ -46,8 +45,6 @@ if [[ -n $TAG_NAME ]]; then
|
||||
git add "$TAG_NAME"
|
||||
# Update the symlink
|
||||
git add stable
|
||||
# Update versions file
|
||||
git add versions.json
|
||||
git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
|
||||
elif [[ $BETA = "true" ]]; then
|
||||
if git diff --exit-code --quiet -- beta/; then
|
||||
|
4
.github/workflows/clippy_bors.yml
vendored
4
.github/workflows/clippy_bors.yml
vendored
@ -162,7 +162,7 @@ jobs:
|
||||
find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
|
||||
|
||||
- name: Upload Binaries
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries
|
||||
path: target/debug
|
||||
@ -202,7 +202,7 @@ jobs:
|
||||
|
||||
# Download
|
||||
- name: Download target dir
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binaries
|
||||
path: target/debug
|
||||
|
65
CHANGELOG.md
65
CHANGELOG.md
@ -6,7 +6,61 @@ document.
|
||||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master)
|
||||
[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master)
|
||||
|
||||
## Rust 1.81
|
||||
|
||||
Current stable, released 2024-09-05
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`cfg_not_test`] to `restriction`
|
||||
[#11293](https://github.com/rust-lang/rust-clippy/pull/11293)
|
||||
* Added [`byte_char_slices`] to `style`
|
||||
[#10155](https://github.com/rust-lang/rust-clippy/pull/10155)
|
||||
* Added [`set_contains_or_insert`] to `nursery`
|
||||
[#12873](https://github.com/rust-lang/rust-clippy/pull/12873)
|
||||
* Added [`manual_rotate`] to `style`
|
||||
[#12983](https://github.com/rust-lang/rust-clippy/pull/12983)
|
||||
* Added [`unnecessary_min_or_max`] to `complexity`
|
||||
[#12368](https://github.com/rust-lang/rust-clippy/pull/12368)
|
||||
* Added [`manual_inspect`] to `complexity`
|
||||
[#12287](https://github.com/rust-lang/rust-clippy/pull/12287)
|
||||
* Added [`field_scoped_visibility_modifiers`] to `restriction`
|
||||
[#12893](https://github.com/rust-lang/rust-clippy/pull/12893)
|
||||
* Added [`manual_pattern_char_comparison`] to `style`
|
||||
[#12849](https://github.com/rust-lang/rust-clippy/pull/12849)
|
||||
* Added [`needless_maybe_sized`] to `suspicious`
|
||||
[#10632](https://github.com/rust-lang/rust-clippy/pull/10632)
|
||||
* Added [`needless_character_iteration`] to `suspicious`
|
||||
[#12815](https://github.com/rust-lang/rust-clippy/pull/12815)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable
|
||||
[rust#120924](https://github.com/rust-lang/rust/pull/120924)
|
||||
* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`]
|
||||
[#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
|
||||
* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default)
|
||||
[#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
|
||||
* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`]
|
||||
[#12974](https://github.com/rust-lang/rust-clippy/pull/12974)
|
||||
* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo
|
||||
and rustc
|
||||
[#12875](https://github.com/rust-lang/rust-clippy/pull/12875)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let`
|
||||
expressions
|
||||
[#12870](https://github.com/rust-lang/rust-clippy/pull/12870)
|
||||
* [`std_instead_of_core`]: Now respects the `msrv` configuration
|
||||
[#13168](https://github.com/rust-lang/rust-clippy/pull/13168)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions
|
||||
[#12884](https://github.com/rust-lang/rust-clippy/pull/12884)
|
||||
|
||||
## Rust 1.80
|
||||
|
||||
@ -5500,6 +5554,7 @@ Released 2018-09-13
|
||||
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
|
||||
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
|
||||
[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
|
||||
[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
|
||||
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
|
||||
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
|
||||
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
|
||||
@ -5559,6 +5614,7 @@ Released 2018-09-13
|
||||
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
|
||||
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
|
||||
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
|
||||
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
|
||||
[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
|
||||
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
|
||||
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
|
||||
@ -5570,6 +5626,7 @@ Released 2018-09-13
|
||||
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
|
||||
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
|
||||
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
|
||||
[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two
|
||||
[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
|
||||
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
|
||||
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
|
||||
@ -5716,6 +5773,7 @@ Released 2018-09-13
|
||||
[`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg
|
||||
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
|
||||
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
|
||||
[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions
|
||||
[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
|
||||
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
|
||||
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
|
||||
@ -5757,6 +5815,7 @@ Released 2018-09-13
|
||||
[`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
|
||||
[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
|
||||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
@ -5962,6 +6021,7 @@ Released 2018-09-13
|
||||
[`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
|
||||
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
|
||||
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
|
||||
[`unnecessary_first_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_first_then_check
|
||||
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
|
||||
[`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
|
||||
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
||||
@ -6003,6 +6063,7 @@ Released 2018-09-13
|
||||
[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
|
||||
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_trait_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
|
||||
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
|
||||
@ -6013,6 +6074,7 @@ Released 2018-09-13
|
||||
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
|
||||
[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
|
||||
[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
|
||||
[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items
|
||||
[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
|
||||
[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
|
||||
[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
|
||||
@ -6047,6 +6109,7 @@ Released 2018-09-13
|
||||
[`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects
|
||||
[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
|
||||
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
|
||||
[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes
|
||||
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
|
||||
<!-- end autogenerated links to lint list -->
|
||||
<!-- begin autogenerated links to configuration documentation -->
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
@ -31,7 +31,7 @@ anstream = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
cargo_metadata = "0.18.1"
|
||||
ui_test = "0.25"
|
||||
ui_test = "0.26.4"
|
||||
regex = "1.5.5"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
@ -67,3 +67,10 @@ harness = false
|
||||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
@ -727,6 +727,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
||||
* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
|
||||
* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
|
||||
* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
|
||||
* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names)
|
||||
* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::ClippyConfiguration;
|
||||
use crate::msrvs::Msrv;
|
||||
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
|
||||
use crate::ClippyConfiguration;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edit_distance::edit_distance;
|
||||
@ -563,6 +563,7 @@ define_Conf! {
|
||||
uninlined_format_args,
|
||||
unnecessary_lazy_evaluations,
|
||||
unnested_or_patterns,
|
||||
unused_trait_names,
|
||||
use_self,
|
||||
)]
|
||||
msrv: Msrv = Msrv::empty(),
|
||||
@ -864,7 +865,7 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
|
||||
cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
|
||||
});
|
||||
|
||||
let rows = (fields.len() + (columns - 1)) / columns;
|
||||
let rows = fields.len().div_ceil(columns);
|
||||
|
||||
let column_widths = (0..columns)
|
||||
.map(|column| {
|
||||
|
@ -26,5 +26,5 @@ mod metadata;
|
||||
pub mod msrvs;
|
||||
pub mod types;
|
||||
|
||||
pub use conf::{get_configuration_metadata, lookup_conf_file, Conf};
|
||||
pub use conf::{Conf, get_configuration_metadata, lookup_conf_file};
|
||||
pub use metadata::ClippyConfiguration;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_attr::parse_version;
|
||||
use rustc_session::{RustcVersion, Session};
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_span::{Symbol, sym};
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
|
||||
@ -23,6 +23,7 @@ msrv_aliases! {
|
||||
1,80,0 { BOX_INTO_ITER}
|
||||
1,77,0 { C_STR_LITERALS }
|
||||
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
|
||||
1,73,0 { MANUAL_DIV_CEIL }
|
||||
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
|
||||
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
|
||||
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
||||
@ -38,7 +39,7 @@ msrv_aliases! {
|
||||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||
1,50,0 { BOOL_THEN, CLAMP }
|
||||
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
|
||||
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
|
||||
1,46,0 { CONST_IF_MATCH }
|
||||
1,45,0 { STR_STRIP_PREFIX }
|
||||
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
|
||||
@ -50,6 +51,7 @@ msrv_aliases! {
|
||||
1,36,0 { ITERATOR_COPIED }
|
||||
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
|
||||
1,34,0 { TRY_FROM }
|
||||
1,33,0 { UNDERSCORE_IMPORTS }
|
||||
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
|
||||
1,29,0 { ITER_FLATTEN }
|
||||
1,28,0 { FROM_BOOL }
|
||||
|
@ -1,5 +1,5 @@
|
||||
use serde::de::{self, Deserializer, Visitor};
|
||||
use serde::{ser, Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize, ser};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::clippy_project_root;
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use shell_escape::escape;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::ops::ControlFlow;
|
||||
|
@ -441,7 +441,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
|
||||
use super::update_lints::{match_tokens, LintDeclSearchResult};
|
||||
use super::update_lints::{LintDeclSearchResult, match_tokens};
|
||||
use rustc_lexer::TokenKind;
|
||||
|
||||
let lint_name_upper = lint.name.to_uppercase();
|
||||
|
@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
|
||||
}
|
||||
if let Some(url) = url.take() {
|
||||
thread::spawn(move || {
|
||||
Command::new(PYTHON)
|
||||
let mut child = Command::new(PYTHON)
|
||||
.arg("-m")
|
||||
.arg("http.server")
|
||||
.arg(port.to_string())
|
||||
@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
// Launch browser after first export.py has completed and http.server is up
|
||||
let _result = opener::open(url);
|
||||
child.wait().unwrap();
|
||||
});
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::clippy_project_root;
|
||||
use aho_corasick::AhoCorasickBuilder;
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
|
||||
use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::{self, Write};
|
||||
@ -1048,23 +1048,17 @@ mod tests {
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
];
|
||||
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
|
||||
expected.insert(
|
||||
"group1".to_string(),
|
||||
vec![
|
||||
expected.insert("group1".to_string(), vec![
|
||||
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
|
||||
],
|
||||
);
|
||||
expected.insert(
|
||||
"group2".to_string(),
|
||||
vec![Lint::new(
|
||||
]);
|
||||
expected.insert("group2".to_string(), vec![Lint::new(
|
||||
"should_assert_eq2",
|
||||
"group2",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
)],
|
||||
);
|
||||
)]);
|
||||
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -3,12 +3,12 @@ use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
|
||||
use rustc_hir::def_id::{CRATE_DEF_INDEX, DefId};
|
||||
use rustc_hir::{HirId, ItemKind, Node, Path};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{trim_span, walk_span_to_context};
|
||||
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
|
||||
|
@ -1,10 +1,10 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{impl_lint_pass, RustcVersion};
|
||||
use rustc_session::{RustcVersion, impl_lint_pass};
|
||||
use rustc_span::symbol;
|
||||
use std::f64::consts as f64;
|
||||
|
||||
|
@ -4,8 +4,8 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_inside_always_const_context;
|
||||
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
|
||||
use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
|
||||
use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
|
||||
use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
|
||||
use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_ast::{MetaItemKind, NestedMetaItem};
|
||||
@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet
|
||||
cx,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
attr.span,
|
||||
format!("`{}` attribute without specifying a reason", name.as_str()),
|
||||
format!("`{name}` attribute without specifying a reason"),
|
||||
|diag| {
|
||||
diag.help("try adding a reason at the end with `, reason = \"..\"`");
|
||||
},
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::utils::extract_clippy_lint;
|
||||
use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
|
||||
use super::utils::extract_clippy_lint;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_lint::{LateContext, Level, LintContext};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, DUMMY_SP};
|
||||
use rustc_span::{DUMMY_SP, sym};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
|
||||
for lint in items {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR};
|
||||
use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::AttrStyle;
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::{Attribute, MetaItem};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
fn emit_if_duplicated(
|
||||
@ -36,7 +36,7 @@ fn check_duplicated_attr(
|
||||
}
|
||||
let Some(ident) = attr.ident() else { return };
|
||||
let name = ident.name;
|
||||
if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented {
|
||||
if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason {
|
||||
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
||||
// conditions are the same.
|
||||
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
||||
|
@ -1,52 +0,0 @@
|
||||
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
|
||||
use rustc_ast::{AttrKind, AttrStyle};
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
/// Check for empty lines after outer attributes.
|
||||
///
|
||||
/// Attributes and documentation comments are both considered outer attributes
|
||||
/// by the AST. However, the average user likely considers them to be different.
|
||||
/// Checking for empty lines after each of these attributes is split into two different
|
||||
/// lints but can share the same logic.
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
|
||||
let mut iter = item.attrs.iter().peekable();
|
||||
while let Some(attr) = iter.next() {
|
||||
if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..)))
|
||||
&& attr.style == AttrStyle::Outer
|
||||
&& is_present_in_source(cx, attr.span)
|
||||
{
|
||||
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
|
||||
let end_of_attr_to_next_attr_or_item = Span::new(
|
||||
attr.span.hi(),
|
||||
iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
|
||||
item.span.ctxt(),
|
||||
item.span.parent(),
|
||||
);
|
||||
|
||||
if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
|
||||
let lines = snippet.split('\n').collect::<Vec<_>>();
|
||||
let lines = without_block_comments(lines);
|
||||
|
||||
if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
|
||||
let (lint_msg, lint_type) = match attr.kind {
|
||||
AttrKind::DocComment(..) => (
|
||||
"found an empty line after a doc comment. \
|
||||
Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?",
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
),
|
||||
AttrKind::Normal(..) => (
|
||||
"found an empty line after an outer attribute. \
|
||||
Perhaps you forgot to add a `!` to make it an inner attribute?",
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
),
|
||||
};
|
||||
|
||||
span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
use super::utils::is_word;
|
||||
use super::INLINE_ALWAYS;
|
||||
use super::utils::is_word;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
|
||||
if span.from_expansion() {
|
||||
|
@ -4,7 +4,6 @@ mod blanket_clippy_restriction_lints;
|
||||
mod deprecated_cfg_attr;
|
||||
mod deprecated_semver;
|
||||
mod duplicated_attributes;
|
||||
mod empty_line_after;
|
||||
mod inline_always;
|
||||
mod mixed_attributes_style;
|
||||
mod non_minimal_cfg;
|
||||
@ -13,8 +12,8 @@ mod unnecessary_clippy_cfg;
|
||||
mod useless_attribute;
|
||||
mod utils;
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
@ -126,94 +125,6 @@ declare_clippy_lint! {
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Most likely the attribute was meant to be an inner attribute using a '!'.
|
||||
/// If it was meant to be an outer attribute, then the following item
|
||||
/// should not be separated by empty lines.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Can cause false positives.
|
||||
///
|
||||
/// From the clippy side it's difficult to detect empty lines between an attributes and the
|
||||
/// following item because empty lines and comments are not part of the AST. The parsing
|
||||
/// currently works for basic cases but is not perfect.
|
||||
///
|
||||
/// ### 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,
|
||||
nursery,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after documentation comments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The documentation comment was most likely meant to be an inner attribute or regular comment.
|
||||
/// If it was intended to be a documentation comment, then the empty line should be removed to
|
||||
/// be more idiomatic.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only detects empty lines immediately following the documentation. If the doc comment is followed
|
||||
/// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr`
|
||||
/// in combination with this lint to detect both cases.
|
||||
///
|
||||
/// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Some doc comment with a blank line after it.
|
||||
///
|
||||
/// fn not_quite_good_code() { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// Good (no blank line)
|
||||
/// fn this_is_fine() { }
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Good (convert to a regular comment)
|
||||
///
|
||||
/// fn this_is_fine_too() { }
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// //! Good (convert to a comment on an inner attribute)
|
||||
///
|
||||
/// fn this_is_fine_as_well() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
nursery,
|
||||
"empty line after documentation comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
|
||||
@ -601,18 +512,12 @@ impl EarlyAttributes {
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CFG_ATTR,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
NON_MINIMAL_CFG,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
|
||||
empty_line_after::check(cx, item);
|
||||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
deprecated_cfg_attr::check(cx, attr, &self.msrv);
|
||||
deprecated_cfg_attr::check_clippy(cx, attr);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::utils::{extract_clippy_lint, is_lint_level, is_word};
|
||||
use super::{Attribute, USELESS_ATTRIBUTE};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{first_line_of_span, SpanRangeExt};
|
||||
use clippy_utils::source::{SpanRangeExt, first_line_of_span};
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
)
|
||||
}) {
|
||||
return;
|
||||
|
@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::CoroutineLayout;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -4,12 +4,12 @@ use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, Level};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -4,7 +4,7 @@ use clippy_utils::ty::expr_sig;
|
||||
use clippy_utils::{is_default_equivalent, path_def_id};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_ty};
|
||||
use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -22,7 +22,7 @@ declare_clippy_lint! {
|
||||
/// ```ignore
|
||||
/// b"Hello"
|
||||
/// ```
|
||||
#[clippy::version = "1.68.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub BYTE_CHAR_SLICES,
|
||||
style,
|
||||
"hard to read byte char slice"
|
||||
|
@ -2,7 +2,7 @@ use super::LINT_GROUPS_PRIORITY;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{unerased_lint_store, LateContext};
|
||||
use rustc_lint::{LateContext, unerased_lint_store};
|
||||
use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -10,7 +10,7 @@ use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_span::hygiene;
|
||||
|
||||
use super::{utils, CAST_LOSSLESS};
|
||||
use super::{CAST_LOSSLESS, utils};
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
|
@ -12,7 +12,7 @@ use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::IntegerType;
|
||||
|
||||
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
|
||||
use super::{CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION, utils};
|
||||
|
||||
fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
|
||||
if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) {
|
||||
|
@ -3,7 +3,7 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
use super::{utils, CAST_POSSIBLE_WRAP};
|
||||
use super::{CAST_POSSIBLE_WRAP, utils};
|
||||
|
||||
// this should be kept in sync with the allowed bit widths of `usize` and `isize`
|
||||
const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64];
|
||||
|
@ -4,7 +4,7 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
|
||||
use super::{utils, CAST_PRECISION_LOSS};
|
||||
use super::{CAST_PRECISION_LOSS, utils};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
if !cast_from.is_integral() || cast_to.is_integral() {
|
||||
|
@ -3,7 +3,7 @@ use std::ops::ControlFlow;
|
||||
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
|
||||
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
|
||||
use clippy_utils::{method_chain_args, sext};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -5,7 +5,7 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty, UintTy};
|
||||
|
||||
use super::{utils, FN_TO_NUMERIC_CAST};
|
||||
use super::{FN_TO_NUMERIC_CAST, utils};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
// We only want to check casts to `ty::Uint` or `ty::Int`
|
||||
|
@ -5,7 +5,7 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION};
|
||||
use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
// We only want to check casts to `ty::Uint` or `ty::Int`
|
||||
|
@ -23,8 +23,8 @@ mod unnecessary_cast;
|
||||
mod utils;
|
||||
mod zero_ptr;
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
@ -410,19 +410,27 @@ declare_clippy_lint! {
|
||||
/// ### Why is this bad?
|
||||
/// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
|
||||
/// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
|
||||
/// type.
|
||||
/// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr: *const u32 = &42_u32;
|
||||
/// let mut_ptr = ptr as *mut u32;
|
||||
/// let ptr = mut_ptr as *const u32;
|
||||
/// let ptr1 = std::ptr::null::<u32>() as *mut u32;
|
||||
/// let ptr2 = std::ptr::null_mut::<u32>() as *const u32;
|
||||
/// let ptr3 = std::ptr::null::<u32>().cast_mut();
|
||||
/// let ptr4 = std::ptr::null_mut::<u32>().cast_const();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr: *const u32 = &42_u32;
|
||||
/// let mut_ptr = ptr.cast_mut();
|
||||
/// let ptr = mut_ptr.cast_const();
|
||||
/// let ptr1 = std::ptr::null_mut::<u32>();
|
||||
/// let ptr2 = std::ptr::null::<u32>();
|
||||
/// let ptr3 = std::ptr::null_mut::<u32>();
|
||||
/// let ptr4 = std::ptr::null::<u32>();
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub PTR_CAST_CONSTNESS,
|
||||
@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
char_lit_as_u8::check(cx, expr);
|
||||
ptr_as_ptr::check(cx, expr, &self.msrv);
|
||||
cast_slice_different_sizes::check(cx, expr, &self.msrv);
|
||||
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
|
@ -1,10 +1,12 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::std_or_core;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::PTR_CAST_CONSTNESS;
|
||||
|
||||
@ -16,8 +18,7 @@ pub(super) fn check<'tcx>(
|
||||
cast_to: Ty<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
|
||||
&& let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
|
||||
if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
|
||||
&& let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
|
||||
&& matches!(
|
||||
(from_mutbl, to_mutbl),
|
||||
@ -26,6 +27,32 @@ pub(super) fn check<'tcx>(
|
||||
&& from_ty == to_ty
|
||||
&& !from_ty.has_erased_regions()
|
||||
{
|
||||
if let ExprKind::Call(func, []) = cast_expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
|
||||
&& let Some(defid) = path.res.opt_def_id()
|
||||
&& let Some(prefix) = std_or_core(cx)
|
||||
&& let mut app = Applicability::MachineApplicable
|
||||
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
|
||||
&& let Some((_, after_lt)) = sugg.split_once("::<")
|
||||
&& let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
|
||||
Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
|
||||
Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")),
|
||||
_ => None,
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_CAST_CONSTNESS,
|
||||
expr.span,
|
||||
format!("`as` casting to make a {source} null pointer into a {target} null pointer"),
|
||||
format!("use `{target_func}()` directly instead"),
|
||||
format!("{prefix}::ptr::{target_func}::<{after_lt}"),
|
||||
app,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) {
|
||||
let sugg = Sugg::hir(cx, cast_expr, "_");
|
||||
let constness = match *to_mutbl {
|
||||
Mutability::Not => "const",
|
||||
@ -43,3 +70,31 @@ pub(super) fn check<'tcx>(
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
|
||||
&& let ExprKind::Call(func, []) = cast_expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
|
||||
&& let Some(defid) = path.res.opt_def_id()
|
||||
&& let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) {
|
||||
(Some(sym::ptr_null), "cast_mut") => "null_mut",
|
||||
(Some(sym::ptr_null_mut), "cast_const") => "null",
|
||||
_ => return,
|
||||
}
|
||||
&& let Some(prefix) = std_or_core(cx)
|
||||
&& let mut app = Applicability::MachineApplicable
|
||||
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
|
||||
&& let Some((_, after_lt)) = sugg.split_once("::<")
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_CAST_CONSTNESS,
|
||||
expr.span,
|
||||
"changing constness of a null pointer",
|
||||
format!("use `{method}()` directly instead"),
|
||||
format!("{prefix}::ptr::{method}::<{after_lt}"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{expr_use_ctxt, is_no_std_crate, ExprUseNode};
|
||||
use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::numeric_literal::NumericLiteral;
|
||||
use clippy_utils::source::{snippet_opt, SpanRangeExt};
|
||||
use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_opt};
|
||||
use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
|
||||
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
|
||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::ty::{read_explicit_enum_value, EnumValue};
|
||||
use clippy_utils::ty::{EnumValue, read_explicit_enum_value};
|
||||
use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
|
||||
|
||||
/// Returns the size in bits of an integral type.
|
||||
|
@ -22,7 +22,7 @@ declare_clippy_lint! {
|
||||
/// # fn important_check() {}
|
||||
/// important_check();
|
||||
/// ```
|
||||
#[clippy::version = "1.73.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub CFG_NOT_TEST,
|
||||
restriction,
|
||||
"enforce against excluding code from test builds"
|
||||
|
@ -1,8 +1,8 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq};
|
||||
use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
|
||||
use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
@ -11,7 +11,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
|
||||
use clippy_utils::visitors::{for_each_expr, Visitable};
|
||||
use clippy_utils::visitors::{Visitable, for_each_expr};
|
||||
use clippy_utils::{get_enclosing_block, path_to_local_id};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq};
|
||||
use clippy_utils::{SpanlessEq, if_sequence, is_else_clause, is_in_const_context};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -1,16 +1,17 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
||||
use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet};
|
||||
use clippy_utils::ty::{InteriorMut, needs_ordered_drop};
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{
|
||||
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
|
||||
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init,
|
||||
get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local,
|
||||
search_same,
|
||||
};
|
||||
use core::iter;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{macro_backtrace, MacroCall};
|
||||
use clippy_utils::macros::{MacroCall, macro_backtrace};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
@ -9,7 +9,7 @@ use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span, SyntaxContext};
|
||||
use rustc_span::{Span, SyntaxContext, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -49,8 +49,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_SEMVER_INFO,
|
||||
crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::attrs::INLINE_ALWAYS_INFO,
|
||||
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
|
||||
crate::attrs::NON_MINIMAL_CFG_INFO,
|
||||
@ -138,6 +136,8 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||
crate::doc::DOC_MARKDOWN_INFO,
|
||||
crate::doc::EMPTY_DOCS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::doc::MISSING_ERRORS_DOC_INFO,
|
||||
crate::doc::MISSING_PANICS_DOC_INFO,
|
||||
crate::doc::MISSING_SAFETY_DOC_INFO,
|
||||
@ -217,6 +217,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::implicit_return::IMPLICIT_RETURN_INFO,
|
||||
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
|
||||
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
|
||||
crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO,
|
||||
crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
|
||||
crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO,
|
||||
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
|
||||
@ -300,10 +301,12 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
|
||||
crate::manual_bits::MANUAL_BITS_INFO,
|
||||
crate::manual_clamp::MANUAL_CLAMP_INFO,
|
||||
crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
||||
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
|
||||
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
|
||||
crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
|
||||
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
|
||||
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
|
||||
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
|
||||
@ -464,6 +467,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
|
||||
crate::methods::UNNECESSARY_FILTER_MAP_INFO,
|
||||
crate::methods::UNNECESSARY_FIND_MAP_INFO,
|
||||
crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO,
|
||||
crate::methods::UNNECESSARY_FOLD_INFO,
|
||||
crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO,
|
||||
crate::methods::UNNECESSARY_JOIN_INFO,
|
||||
@ -486,6 +490,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
|
||||
crate::misc::TOPLEVEL_REF_ARG_INFO,
|
||||
crate::misc::USED_UNDERSCORE_BINDING_INFO,
|
||||
crate::misc::USED_UNDERSCORE_ITEMS_INFO,
|
||||
crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
|
||||
crate::misc_early::DOUBLE_NEG_INFO,
|
||||
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
|
||||
@ -553,6 +558,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::non_expressive_names::SIMILAR_NAMES_INFO,
|
||||
crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
|
||||
crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
|
||||
crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
|
||||
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
|
||||
crate::octal_escapes::OCTAL_ESCAPES_INFO,
|
||||
crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
|
||||
@ -598,6 +604,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO,
|
||||
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
|
||||
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
|
||||
crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
|
||||
crate::precedence::PRECEDENCE_INFO,
|
||||
crate::ptr::CMP_NULL_INFO,
|
||||
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
|
||||
@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
|
||||
crate::unused_rounding::UNUSED_ROUNDING_INFO,
|
||||
crate::unused_self::UNUSED_SELF_INFO,
|
||||
crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO,
|
||||
crate::unused_unit::UNUSED_UNIT_INFO,
|
||||
crate::unwrap::PANICKING_UNWRAP_INFO,
|
||||
crate::unwrap::UNNECESSARY_UNWRAP_INFO,
|
||||
@ -767,4 +775,5 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
|
||||
crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO,
|
||||
crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
|
||||
crate::zombie_processes::ZOMBIE_PROCESSES_INFO,
|
||||
];
|
||||
|
@ -11,7 +11,7 @@ use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_ty_alias;
|
||||
use hir::def::Res;
|
||||
use hir::ExprKind;
|
||||
use hir::def::Res;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{last_path_segment, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
|
||||
use rustc_hir::{Expr, ExprKind, GenericArg, QPath, TyKind, def};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::numeric_literal;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
|
||||
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -3,14 +3,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{implements_trait, is_manually_drop};
|
||||
use clippy_utils::{
|
||||
expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
|
||||
ExprUseNode,
|
||||
DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local,
|
||||
peel_middle_ty_refs,
|
||||
};
|
||||
use core::mem;
|
||||
use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_ty};
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
|
||||
PatKind, Path, QPath, TyKind, UnOp,
|
||||
@ -290,13 +290,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||
&& let Some(ty) = use_node.defined_ty(cx)
|
||||
&& TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable()
|
||||
{
|
||||
self.state = Some((
|
||||
State::ExplicitDeref { mutability: None },
|
||||
StateData {
|
||||
self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
|
||||
first_expr: expr,
|
||||
adjusted_ty,
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
},
|
||||
RefOp::Method { mutbl, is_ufcs }
|
||||
@ -458,13 +455,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||
&& next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
|
||||
&& iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
|
||||
{
|
||||
self.state = Some((
|
||||
State::Borrow { mutability },
|
||||
StateData {
|
||||
self.state = Some((State::Borrow { mutability }, StateData {
|
||||
first_expr: expr,
|
||||
adjusted_ty,
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
@ -508,13 +502,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||
let stability = state.stability;
|
||||
report(cx, expr, State::DerefedBorrow(state), data, typeck);
|
||||
if stability.is_deref_stable() {
|
||||
self.state = Some((
|
||||
State::Borrow { mutability },
|
||||
StateData {
|
||||
self.state = Some((State::Borrow { mutability }, StateData {
|
||||
first_expr: expr,
|
||||
adjusted_ty,
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
},
|
||||
(Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
|
||||
@ -539,13 +530,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||
} else if stability.is_deref_stable()
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
{
|
||||
self.state = Some((
|
||||
State::ExplicitDeref { mutability: None },
|
||||
StateData {
|
||||
self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
|
||||
first_expr: parent,
|
||||
adjusted_ty,
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
@ -1138,9 +1126,7 @@ impl<'tcx> Dereferencing<'tcx> {
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
|
||||
if let Some(pat) = outer_pat {
|
||||
// Check for auto-deref
|
||||
if !matches!(
|
||||
cx.typeck_results().expr_adjustments(e),
|
||||
[
|
||||
if !matches!(cx.typeck_results().expr_adjustments(e), [
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
@ -1150,8 +1136,7 @@ impl<'tcx> Dereferencing<'tcx> {
|
||||
..
|
||||
},
|
||||
..
|
||||
]
|
||||
) {
|
||||
]) {
|
||||
match get_parent_expr(cx, e) {
|
||||
// Field accesses are the same no matter the number of references.
|
||||
Some(Expr {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::indent_of;
|
||||
use clippy_utils::{is_default_equivalent, peel_blocks};
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
||||
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
|
||||
use rustc_hir::{
|
||||
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource,
|
||||
};
|
||||
@ -15,7 +15,7 @@ use rustc_middle::ty::{
|
||||
};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
342
clippy_lints/src/doc/empty_line_after.rs
Normal file
342
clippy_lints/src/doc/empty_line_after.rs
Normal file
@ -0,0 +1,342 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_indent};
|
||||
use clippy_utils::tokenize_with_text;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute};
|
||||
use rustc_errors::{Applicability, Diag, SuggestionStyle};
|
||||
use rustc_hir::{ItemKind, Node};
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData};
|
||||
|
||||
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum StopKind {
|
||||
Attr,
|
||||
Doc(CommentKind),
|
||||
}
|
||||
|
||||
impl StopKind {
|
||||
fn is_doc(self) -> bool {
|
||||
matches!(self, StopKind::Doc(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Stop {
|
||||
span: Span,
|
||||
kind: StopKind,
|
||||
first: usize,
|
||||
last: usize,
|
||||
}
|
||||
|
||||
impl Stop {
|
||||
fn convert_to_inner(&self) -> (Span, String) {
|
||||
let inner = match self.kind {
|
||||
// #|[...]
|
||||
StopKind::Attr => InnerSpan::new(1, 1),
|
||||
// /// or /**
|
||||
// ^ ^
|
||||
StopKind::Doc(_) => InnerSpan::new(2, 3),
|
||||
};
|
||||
(self.span.from_inner(inner), "!".into())
|
||||
}
|
||||
|
||||
fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) {
|
||||
match self.kind {
|
||||
StopKind::Attr => {
|
||||
if cx.tcx.sess.source_map().is_multiline(self.span) {
|
||||
suggestions.extend([
|
||||
(self.span.shrink_to_lo(), "/* ".into()),
|
||||
(self.span.shrink_to_hi(), " */".into()),
|
||||
]);
|
||||
} else {
|
||||
suggestions.push((self.span.shrink_to_lo(), "// ".into()));
|
||||
}
|
||||
},
|
||||
StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())),
|
||||
StopKind::Doc(CommentKind::Block) => {
|
||||
// /** outer */ /*! inner */
|
||||
// ^ ^
|
||||
let asterisk = self.span.from_inner(InnerSpan::new(1, 2));
|
||||
suggestions.push((asterisk, String::new()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option<Self> {
|
||||
let SpanData { lo, hi, .. } = attr.span.data();
|
||||
let file = cx.tcx.sess.source_map().lookup_source_file(lo);
|
||||
|
||||
Some(Self {
|
||||
span: attr.span,
|
||||
kind: match attr.kind {
|
||||
AttrKind::Normal(_) => StopKind::Attr,
|
||||
AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind),
|
||||
},
|
||||
first: file.lookup_line(file.relative_position(lo))?,
|
||||
last: file.lookup_line(file.relative_position(hi))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a set of attrs/doc comments separated by 1 or more empty lines
|
||||
///
|
||||
/// ```ignore
|
||||
/// /// chunk 1 docs
|
||||
/// // not an empty line so also part of chunk 1
|
||||
/// #[chunk_1_attrs] // <-- prev_stop
|
||||
///
|
||||
/// /* gap */
|
||||
///
|
||||
/// /// chunk 2 docs // <-- next_stop
|
||||
/// #[chunk_2_attrs]
|
||||
/// ```
|
||||
struct Gap<'a> {
|
||||
/// The span of individual empty lines including the newline at the end of the line
|
||||
empty_lines: Vec<Span>,
|
||||
has_comment: bool,
|
||||
next_stop: &'a Stop,
|
||||
prev_stop: &'a Stop,
|
||||
/// The chunk that includes [`prev_stop`](Self::prev_stop)
|
||||
prev_chunk: &'a [Stop],
|
||||
}
|
||||
|
||||
impl<'a> Gap<'a> {
|
||||
fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option<Self> {
|
||||
let prev_stop = prev_chunk.last()?;
|
||||
let next_stop = next_chunk.first()?;
|
||||
let gap_span = prev_stop.span.between(next_stop.span);
|
||||
let gap_snippet = gap_span.get_source_text(cx)?;
|
||||
|
||||
let mut has_comment = false;
|
||||
let mut empty_lines = Vec::new();
|
||||
|
||||
for (token, source, inner_span) in tokenize_with_text(&gap_snippet) {
|
||||
match token {
|
||||
TokenKind::BlockComment {
|
||||
doc_style: None,
|
||||
terminated: true,
|
||||
}
|
||||
| TokenKind::LineComment { doc_style: None } => has_comment = true,
|
||||
TokenKind::Whitespace => {
|
||||
let newlines = source.bytes().positions(|b| b == b'\n');
|
||||
empty_lines.extend(
|
||||
newlines
|
||||
.tuple_windows()
|
||||
.map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b))
|
||||
.map(|inner_span| gap_span.from_inner(inner_span)),
|
||||
);
|
||||
},
|
||||
// Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro
|
||||
// shenanigans
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
(!empty_lines.is_empty()).then_some(Self {
|
||||
empty_lines,
|
||||
has_comment,
|
||||
next_stop,
|
||||
prev_stop,
|
||||
prev_chunk,
|
||||
})
|
||||
}
|
||||
|
||||
fn contiguous_empty_lines(&self) -> impl Iterator<Item = Span> + '_ {
|
||||
self.empty_lines
|
||||
// The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1".
|
||||
.chunk_by(|a, b| a.hi() + BytePos(1) == b.lo())
|
||||
.map(|chunk| {
|
||||
let first = chunk.first().expect("at least one empty line");
|
||||
let last = chunk.last().expect("at least one empty line");
|
||||
// The BytePos subtraction here is safe, as before an empty line, there must be at least one
|
||||
// attribute/comment. The span needs to start at the end of the previous line.
|
||||
first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// If the node the attributes/docs apply to is the first in the module/crate suggest converting
|
||||
/// them to inner attributes/docs
|
||||
fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) {
|
||||
let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else {
|
||||
return;
|
||||
};
|
||||
let parent_desc = match cx.tcx.parent_hir_node(owner.into()) {
|
||||
Node::Item(item)
|
||||
if let ItemKind::Mod(parent_mod) = item.kind
|
||||
&& let [first, ..] = parent_mod.item_ids
|
||||
&& first.owner_id == owner =>
|
||||
{
|
||||
"parent module"
|
||||
},
|
||||
Node::Crate(crate_mod)
|
||||
if let Some(first) = crate_mod
|
||||
.item_ids
|
||||
.iter()
|
||||
.map(|&id| cx.tcx.hir().item(id))
|
||||
// skip prelude imports
|
||||
.find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
|
||||
&& first.owner_id == owner =>
|
||||
{
|
||||
"crate"
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
diag.multipart_suggestion_verbose(
|
||||
match kind {
|
||||
StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"),
|
||||
StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"),
|
||||
},
|
||||
gaps.iter()
|
||||
.flat_map(|gap| gap.prev_chunk)
|
||||
.map(Stop::convert_to_inner)
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
|
||||
let Some(first_gap) = gaps.first() else {
|
||||
return false;
|
||||
};
|
||||
let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
|
||||
let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines);
|
||||
let mut has_comment = false;
|
||||
let mut has_attr = false;
|
||||
for gap in gaps {
|
||||
has_comment |= gap.has_comment;
|
||||
if !has_attr {
|
||||
has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr);
|
||||
}
|
||||
}
|
||||
let kind = first_gap.prev_stop.kind;
|
||||
let (lint, kind_desc) = match kind {
|
||||
StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"),
|
||||
StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"),
|
||||
};
|
||||
let (lines, are, them) = if empty_lines().nth(1).is_some() {
|
||||
("lines", "are", "them")
|
||||
} else {
|
||||
("line", "is", "it")
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
first_gap.prev_stop.span.to(empty_lines().last().unwrap()),
|
||||
format!("empty {lines} after {kind_desc}"),
|
||||
|diag| {
|
||||
if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() {
|
||||
let def_id = owner.to_def_id();
|
||||
let def_descr = cx.tcx.def_descr(def_id);
|
||||
diag.span_label(cx.tcx.def_span(def_id), match kind {
|
||||
StopKind::Attr => format!("the attribute applies to this {def_descr}"),
|
||||
StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
|
||||
});
|
||||
}
|
||||
|
||||
diag.multipart_suggestion_with_style(
|
||||
format!("if the empty {lines} {are} unintentional remove {them}"),
|
||||
contiguous_empty_lines()
|
||||
.map(|empty_lines| (empty_lines, String::new()))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::HideCodeAlways,
|
||||
);
|
||||
|
||||
if has_comment && kind.is_doc() {
|
||||
// Likely doc comments that applied to some now commented out code
|
||||
//
|
||||
// /// Old docs for Foo
|
||||
// // struct Foo;
|
||||
|
||||
let mut suggestions = Vec::new();
|
||||
for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
|
||||
stop.comment_out(cx, &mut suggestions);
|
||||
}
|
||||
let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) {
|
||||
Some(name) => format!("`{name}`"),
|
||||
None => "this".into(),
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the doc comment should not document {name} comment it out"),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
suggest_inner(cx, diag, kind, gaps);
|
||||
}
|
||||
|
||||
if kind == StopKind::Doc(CommentKind::Line)
|
||||
&& gaps
|
||||
.iter()
|
||||
.all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line))
|
||||
{
|
||||
// Commentless empty gaps between line doc comments, possibly intended to be part of the markdown
|
||||
|
||||
let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default();
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the documentation should include the empty {lines} include {them} in the comment"),
|
||||
empty_lines()
|
||||
.map(|empty_line| (empty_line, format!("{indent}///")))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
kind.is_doc()
|
||||
}
|
||||
|
||||
/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment
|
||||
/// lints where they would be confusing
|
||||
///
|
||||
/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return
|
||||
/// `true` if it triggers
|
||||
pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
|
||||
let mut outer = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion())
|
||||
.map(|attr| Stop::from_attr(cx, attr))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
|
||||
if outer.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Push a fake attribute Stop for the item itself so we check for gaps between the last outer
|
||||
// attr/doc comment and the item they apply to
|
||||
let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs);
|
||||
if !span.from_expansion()
|
||||
&& let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo())
|
||||
{
|
||||
outer.push(Stop {
|
||||
span,
|
||||
kind: StopKind::Attr,
|
||||
first: line.line,
|
||||
// last doesn't need to be accurate here, we don't compare it with anything
|
||||
last: line.line,
|
||||
});
|
||||
}
|
||||
|
||||
let mut gaps = Vec::new();
|
||||
let mut last = 0;
|
||||
for pos in outer
|
||||
.array_windows()
|
||||
.positions(|[a, b]| b.first.saturating_sub(a.last) > 1)
|
||||
{
|
||||
// we want to be after the first stop in the window
|
||||
let pos = pos + 1;
|
||||
if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) {
|
||||
last = pos;
|
||||
gaps.push(gap);
|
||||
}
|
||||
}
|
||||
|
||||
check_gaps(cx, &gaps)
|
||||
}
|
@ -22,7 +22,6 @@ pub(super) fn check(
|
||||
range: Range<usize>,
|
||||
mut span: Span,
|
||||
containers: &[super::Container],
|
||||
line_break_span: Span,
|
||||
) {
|
||||
if doc[range.clone()].contains('\t') {
|
||||
// We don't do tab stops correctly.
|
||||
@ -52,29 +51,6 @@ pub(super) fn check(
|
||||
"doc list item without indentation"
|
||||
};
|
||||
span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
|
||||
let snippet = clippy_utils::source::snippet(cx, line_break_span, "");
|
||||
if snippet.chars().filter(|&c| c == '\n').count() > 1
|
||||
&& let Some(doc_comment_start) = snippet.rfind('\n')
|
||||
&& let doc_comment = snippet[doc_comment_start..].trim()
|
||||
&& (doc_comment == "///" || doc_comment == "//!")
|
||||
{
|
||||
// suggest filling in a blank line
|
||||
diag.span_suggestion_verbose(
|
||||
line_break_span.shrink_to_lo(),
|
||||
"if this should be its own paragraph, add a blank doc comment line",
|
||||
format!("\n{doc_comment}"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if ccount > 0 || blockquote_level > 0 {
|
||||
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
|
||||
} else {
|
||||
let indent = list_indentation - lcount;
|
||||
diag.help(format!(
|
||||
"if this is intended to be part of the list, indent {indent} spaces"
|
||||
));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ccount == 0 && blockquote_level == 0 {
|
||||
// simpler suggestion style for indentation
|
||||
let indent = list_indentation - lcount;
|
||||
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::{Fragments, DOC_LINK_WITH_QUOTES};
|
||||
use super::{DOC_LINK_WITH_QUOTES, Fragments};
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) {
|
||||
if ((trimmed_text.starts_with('\'') && trimmed_text.ends_with('\''))
|
||||
|
@ -5,7 +5,7 @@ use clippy_utils::{is_doc_hidden, return_ty};
|
||||
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
|
@ -23,15 +23,16 @@ use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_resolve::rustdoc::{
|
||||
add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments,
|
||||
DocFragment,
|
||||
DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range,
|
||||
span_of_fragments,
|
||||
};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
use std::ops::Range;
|
||||
use url::Url;
|
||||
|
||||
mod empty_line_after;
|
||||
mod link_with_quotes;
|
||||
mod markdown;
|
||||
mod missing_headers;
|
||||
@ -449,13 +450,88 @@ declare_clippy_lint! {
|
||||
/// /// and probably spanning a many rows.
|
||||
/// struct Foo {}
|
||||
/// ```
|
||||
#[clippy::version = "1.81.0"]
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
style,
|
||||
"ensure that the first line of a documentation paragraph isn't too long"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The doc comment may have meant to be an inner doc comment, regular
|
||||
/// comment or applied to some old code that is now commented out. If it was
|
||||
/// intended to be a doc comment, then the empty line should be removed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Some doc comment with a blank line after it.
|
||||
///
|
||||
/// fn f() {}
|
||||
///
|
||||
/// /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! Convert it to an inner doc comment
|
||||
///
|
||||
/// // Or a regular comment
|
||||
///
|
||||
/// /// Or remove the empty line
|
||||
/// fn f() {}
|
||||
///
|
||||
/// // /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
suspicious,
|
||||
"empty line after doc comments"
|
||||
}
|
||||
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
check_private_items: bool,
|
||||
@ -482,6 +558,8 @@ impl_lint_pass!(Documentation => [
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
]);
|
||||
|
||||
@ -612,12 +690,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
||||
Some(("fake".into(), "fake".into()))
|
||||
}
|
||||
|
||||
if is_doc_hidden(attrs) {
|
||||
if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) {
|
||||
return None;
|
||||
}
|
||||
|
||||
suspicious_doc_comments::check(cx, attrs);
|
||||
|
||||
let (fragments, _) = attrs_to_doc_fragments(
|
||||
attrs.iter().filter_map(|attr| {
|
||||
if in_external_macro(cx.sess(), attr.span) {
|
||||
@ -816,7 +892,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
range.end..next_range.start,
|
||||
Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
|
||||
&containers[..],
|
||||
span,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -13,7 +13,7 @@ use rustc_parse::parser::ForceCollect;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use rustc_span::{sym, FileName, Pos};
|
||||
use rustc_span::{FileName, Pos, sym};
|
||||
|
||||
use super::Fragments;
|
||||
|
||||
|
@ -7,7 +7,7 @@ use rustc_span::Span;
|
||||
|
||||
use super::SUSPICIOUS_DOC_COMMENTS;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
|
||||
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
|
||||
let replacements: Vec<_> = collect_doc_replacements(attrs);
|
||||
|
||||
if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
|
||||
@ -24,6 +24,10 @@ pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,10 +79,17 @@ pub(super) fn check(
|
||||
&& let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
|
||||
&& let Some(snippet) = snippet_opt(cx, new_span)
|
||||
{
|
||||
let Some(first) = snippet_opt(cx, first_span) else {
|
||||
return;
|
||||
};
|
||||
let Some(comment_form) = first.get(..3) else {
|
||||
return;
|
||||
};
|
||||
|
||||
diag.span_suggestion(
|
||||
new_span,
|
||||
"add an empty line",
|
||||
format!("{snippet}///\n"),
|
||||
format!("{snippet}{comment_form}{snippet}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
|
||||
peel_hir_expr_while, SpanlessEq,
|
||||
SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
|
||||
peel_hir_expr_while,
|
||||
};
|
||||
use core::fmt::{self, Write};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::hir_id::HirIdSet;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Span, SyntaxContext, DUMMY_SP};
|
||||
use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::consts::{mir_to_const, Constant};
|
||||
use clippy_utils::consts::{Constant, mir_to_const};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -1,15 +1,15 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_hir;
|
||||
use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
|
||||
use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit};
|
||||
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
pub struct BoxedLocal {
|
||||
|
@ -5,8 +5,8 @@ use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -2,7 +2,7 @@ use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_ast::node_id::NodeSet;
|
||||
use rustc_ast::visit::{walk_block, walk_item, Visitor};
|
||||
use rustc_ast::visit::{Visitor, walk_block, walk_item};
|
||||
use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage};
|
||||
use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_expn_of, path_def_id};
|
||||
use rustc_errors::Applicability;
|
||||
@ -7,7 +7,7 @@ use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, ExpnId};
|
||||
use rustc_span::{ExpnId, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::{is_from_proc_macro, trait_ref_of_method};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
|
||||
use rustc_hir::{
|
||||
BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
|
||||
PredicateOrigin, Ty, TyKind, WherePredicate,
|
||||
@ -12,8 +12,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -6,7 +6,7 @@ use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -41,7 +41,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub FIELD_SCOPED_VISIBILITY_MODIFIERS,
|
||||
restriction,
|
||||
"checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::consts::Constant::{Int, F32, F64};
|
||||
use clippy_utils::consts::Constant::{F32, F64, Int};
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
|
||||
use clippy_utils::source::{snippet_with_context, SpanRangeExt};
|
||||
use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, root_macro_call_first_node};
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
|
||||
use rustc_errors::Applicability;
|
||||
@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,11 +1,12 @@
|
||||
use arrayvec::ArrayVec;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{
|
||||
find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
|
||||
is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
|
||||
FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
|
||||
format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
|
||||
root_macro_call_first_node,
|
||||
};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
@ -18,11 +19,11 @@ use rustc_errors::Applicability;
|
||||
use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage};
|
||||
use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||
use rustc_ast::{FormatArgsPiece, FormatTrait};
|
||||
use rustc_errors::Applicability;
|
||||
@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -1,11 +1,11 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::span_is_local;
|
||||
use clippy_utils::path_def_id;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_path, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_path};
|
||||
use rustc_hir::{
|
||||
FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path,
|
||||
PathSegment, Ty, TyKind,
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use clippy_utils::{is_in_const_context, is_integer_literal};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -14,8 +14,8 @@ use rustc_hir::intravisit;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use clippy_utils::attrs::is_proc_macro;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rustc_hir::{self as hir, intravisit, HirId, HirIdSet};
|
||||
use rustc_hir::{self as hir, HirId, HirIdSet, intravisit};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
@ -4,8 +4,8 @@ use rustc_hir::def_id::{DefId, DefIdSet};
|
||||
use rustc_hir::hir_id::OwnerId;
|
||||
use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw};
|
||||
|
||||
use super::RENAMED_FUNCTION_PARAMS;
|
||||
|
||||
|
@ -3,11 +3,11 @@ use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo};
|
||||
use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item};
|
||||
|
||||
use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
|
||||
|
||||
|
@ -8,7 +8,7 @@ use rustc_middle::ty::print::PrintTraitRefExt;
|
||||
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
@ -105,6 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
|
||||
let closure = if method_name == "then" { "|| " } else { "" };
|
||||
format!("{closure} {{ {block_snippet}; {arg_snip} }}")
|
||||
} else if method_name == "then" {
|
||||
(std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
|
||||
} else {
|
||||
arg_snip.into_owned()
|
||||
};
|
||||
|
@ -3,18 +3,18 @@ use std::collections::BTreeMap;
|
||||
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
|
||||
use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty};
|
||||
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{Ty, TypeckResults};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -1,11 +1,17 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{
|
||||
SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt,
|
||||
};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -39,7 +45,49 @@ declare_clippy_lint! {
|
||||
"Perform saturating subtraction instead of implicitly checking lower bound of data type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparisons between integers, followed by subtracting the greater value from the
|
||||
/// lower one.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This could result in an underflow and is most likely not what the user wants. If this was
|
||||
/// intended to be a saturated subtraction, consider using the `saturating_sub` method directly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a = 12u32;
|
||||
/// let b = 13u32;
|
||||
///
|
||||
/// let result = if a > b { b - a } else { 0 };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a = 12u32;
|
||||
/// let b = 13u32;
|
||||
///
|
||||
/// let result = a.saturating_sub(b);
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
pub INVERTED_SATURATING_SUB,
|
||||
correctness,
|
||||
"Check if a variable is smaller than another one and still subtract from it even if smaller"
|
||||
}
|
||||
|
||||
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.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
@ -50,9 +98,197 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
|
||||
// Check if the conditional expression is a binary operation
|
||||
&& let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
|
||||
{
|
||||
check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then);
|
||||
} else if let Some(higher::If {
|
||||
cond,
|
||||
then: if_block,
|
||||
r#else: Some(else_block),
|
||||
}) = higher::If::hir(expr)
|
||||
&& let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
|
||||
{
|
||||
check_manual_check(
|
||||
cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_manual_check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
condition: &BinOp,
|
||||
left_hand: &Expr<'tcx>,
|
||||
right_hand: &Expr<'tcx>,
|
||||
if_block: &Expr<'tcx>,
|
||||
else_block: &Expr<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let ty = cx.typeck_results().expr_ty(left_hand);
|
||||
if ty.is_numeric() && !ty.is_signed() {
|
||||
match condition.node {
|
||||
BinOpKind::Gt | BinOpKind::Ge => check_gt(
|
||||
cx,
|
||||
condition.span,
|
||||
expr.span,
|
||||
left_hand,
|
||||
right_hand,
|
||||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
),
|
||||
BinOpKind::Lt | BinOpKind::Le => check_gt(
|
||||
cx,
|
||||
condition.span,
|
||||
expr.span,
|
||||
right_hand,
|
||||
left_hand,
|
||||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_gt(
|
||||
cx: &LateContext<'_>,
|
||||
condition_span: Span,
|
||||
expr_span: Span,
|
||||
big_var: &Expr<'_>,
|
||||
little_var: &Expr<'_>,
|
||||
if_block: &Expr<'_>,
|
||||
else_block: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if let Some(big_var) = Var::new(big_var)
|
||||
&& let Some(little_var) = Var::new(little_var)
|
||||
{
|
||||
check_subtraction(
|
||||
cx,
|
||||
condition_span,
|
||||
expr_span,
|
||||
big_var,
|
||||
little_var,
|
||||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct Var {
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
}
|
||||
|
||||
impl Var {
|
||||
fn new(expr: &Expr<'_>) -> Option<Self> {
|
||||
path_to_local(expr).map(|hir_id| Self {
|
||||
span: expr.span,
|
||||
hir_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_subtraction(
|
||||
cx: &LateContext<'_>,
|
||||
condition_span: Span,
|
||||
expr_span: Span,
|
||||
big_var: Var,
|
||||
little_var: Var,
|
||||
if_block: &Expr<'_>,
|
||||
else_block: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let if_block = peel_blocks(if_block);
|
||||
let else_block = peel_blocks(else_block);
|
||||
if is_integer_literal(if_block, 0) {
|
||||
// We need to check this case as well to prevent infinite recursion.
|
||||
if is_integer_literal(else_block, 0) {
|
||||
// Well, seems weird but who knows?
|
||||
return;
|
||||
}
|
||||
// If the subtraction is done in the `else` block, then we need to also revert the two
|
||||
// variables as it means that the check was reverted too.
|
||||
check_subtraction(
|
||||
cx,
|
||||
condition_span,
|
||||
expr_span,
|
||||
little_var,
|
||||
big_var,
|
||||
else_block,
|
||||
if_block,
|
||||
msrv,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if is_integer_literal(else_block, 0)
|
||||
&& let ExprKind::Binary(op, left, right) = if_block.kind
|
||||
&& let BinOpKind::Sub = op.node
|
||||
{
|
||||
let local_left = path_to_local(left);
|
||||
let local_right = path_to_local(right);
|
||||
if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right {
|
||||
// This part of the condition is voluntarily split from the one before to ensure that
|
||||
// if `snippet_opt` fails, it won't try the next conditions.
|
||||
if let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
|
||||
&& let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
|
||||
&& (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST))
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPLICIT_SATURATING_SUB,
|
||||
expr_span,
|
||||
"manual arithmetic check found",
|
||||
"replace it with",
|
||||
format!("{big_var_snippet}.saturating_sub({little_var_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
} else if Some(little_var.hir_id) == local_left
|
||||
&& Some(big_var.hir_id) == local_right
|
||||
&& let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
|
||||
&& let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
INVERTED_SATURATING_SUB,
|
||||
condition_span,
|
||||
"inverted arithmetic check before subtraction",
|
||||
|diag| {
|
||||
diag.span_note(
|
||||
if_block.span,
|
||||
format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"),
|
||||
);
|
||||
diag.span_suggestion(
|
||||
if_block.span,
|
||||
"try replacing it with",
|
||||
format!("{big_var_snippet} - {little_var_snippet}"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_with_condition<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
cond_op: BinOpKind,
|
||||
cond_left: &Expr<'tcx>,
|
||||
cond_right: &Expr<'tcx>,
|
||||
then: &Expr<'tcx>,
|
||||
) {
|
||||
// Ensure that the binary operator is >, !=, or <
|
||||
&& (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
|
||||
if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op)
|
||||
|
||||
// Check if assign operation is done
|
||||
&& let Some(target) = subtracts_one(cx, then)
|
||||
@ -62,13 +298,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
{
|
||||
// Handle symmetric conditions in the if statement
|
||||
let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
|
||||
if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
|
||||
if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op {
|
||||
(cond_left, cond_right)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
|
||||
if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
|
||||
if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op {
|
||||
(cond_right, cond_left)
|
||||
} else {
|
||||
return;
|
||||
@ -119,7 +355,6 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
|
||||
match peel_blocks_with_stmt(expr).kind {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test;
|
||||
use rustc_attr::{StabilityLevel, StableSince};
|
||||
@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::{impl_lint_pass, RustcVersion};
|
||||
use rustc_session::{RustcVersion, impl_lint_pass};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::IfLet;
|
||||
@ -8,14 +8,14 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
use rustc_span::{BytePos, Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user