mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 07:00:55 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
87ce1d8069
141 changed files with 3554 additions and 1145 deletions
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
|
||||
|
|
60
CHANGELOG.md
60
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
|
||||
|
@ -5757,6 +5812,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
|
||||
|
@ -6013,6 +6069,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 +6104,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 -->
|
||||
|
|
|
@ -36,7 +36,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 }
|
||||
|
|
|
@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
});
|
||||
|
||||
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
|
||||
let mut iter = iter
|
||||
.by_ref()
|
||||
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
|
@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
// matches `!{`
|
||||
match_tokens!(iter, Bang OpenBrace);
|
||||
if let Some(LintDeclSearchResult { range, .. }) =
|
||||
iter.find(|result| result.token == TokenKind::CloseBrace)
|
||||
iter.find(|result| result.token_kind == TokenKind::CloseBrace)
|
||||
{
|
||||
last_decl_curly_offset = Some(range.end);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
@ -486,6 +487,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,
|
||||
|
@ -598,6 +600,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,
|
||||
|
@ -767,4 +770,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,
|
||||
];
|
||||
|
|
329
clippy_lints/src/doc/empty_line_after.rs
Normal file
329
clippy_lints/src/doc/empty_line_after.rs
Normal file
|
@ -0,0 +1,329 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{snippet_indent, SpanRangeExt};
|
||||
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::{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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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}"),
|
||||
empty_lines().map(|empty_line| (empty_line, 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;
|
||||
|
|
|
@ -32,6 +32,7 @@ use rustc_span::{sym, Span};
|
|||
use std::ops::Range;
|
||||
use url::Url;
|
||||
|
||||
mod empty_line_after;
|
||||
mod link_with_quotes;
|
||||
mod markdown;
|
||||
mod missing_headers;
|
||||
|
@ -455,7 +456,82 @@ declare_clippy_lint! {
|
|||
"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,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
|||
let from = self_ty.span.get_source_text(cx)?;
|
||||
let into = target_ty.span.get_source_text(cx)?;
|
||||
|
||||
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
|
||||
.then_some(String::from("Self"))
|
||||
.unwrap_or_default();
|
||||
let mut suggestions = vec![
|
||||
// impl Into<T> for U -> impl From<T> for U
|
||||
// ~~~~ ~~~~
|
||||
|
@ -197,13 +200,10 @@ fn convert_to_from(
|
|||
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
|
||||
// ~~~~ ~~~~
|
||||
(self_ident.span, format!("val: {from}")),
|
||||
];
|
||||
|
||||
if let FnRetTy::Return(_) = sig.decl.output {
|
||||
// fn into(self) -> T -> fn into(self) -> Self
|
||||
// ~ ~~~~
|
||||
suggestions.push((sig.decl.output.span(), String::from("Self")));
|
||||
}
|
||||
(sig.decl.output.span(), return_type),
|
||||
];
|
||||
|
||||
let mut finder = SelfFinder {
|
||||
cx,
|
||||
|
|
|
@ -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::msrvs::{self, Msrv};
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{
|
||||
higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq,
|
||||
};
|
||||
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,73 +98,260 @@ 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
|
||||
|
||||
// Ensure that the binary operator is >, !=, or <
|
||||
&& (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
|
||||
|
||||
// Check if assign operation is done
|
||||
&& let Some(target) = subtracts_one(cx, then)
|
||||
|
||||
// Extracting out the variable name
|
||||
&& let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
|
||||
{
|
||||
// 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 {
|
||||
(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 {
|
||||
(cond_right, cond_left)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
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 <
|
||||
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)
|
||||
|
||||
// Extracting out the variable name
|
||||
&& let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
|
||||
{
|
||||
// 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 || BinOpKind::Ne == cond_op {
|
||||
(cond_left, cond_right)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if the variable in the condition statement is an integer
|
||||
if !cx.typeck_results().expr_ty(cond_var).is_integral() {
|
||||
}
|
||||
} else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
|
||||
if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op {
|
||||
(cond_right, cond_left)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the variable name
|
||||
let var_name = ares_path.segments[0].ident.name.as_str();
|
||||
match cond_num_val.kind {
|
||||
ExprKind::Lit(cond_lit) => {
|
||||
// Check if the constant is zero
|
||||
if let LitKind::Int(Pu128(0), _) = cond_lit.node {
|
||||
if cx.typeck_results().expr_ty(cond_left).is_signed() {
|
||||
} else {
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
};
|
||||
}
|
||||
},
|
||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||
if name.ident.as_str() == "MIN"
|
||||
&& let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(const_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
// Check if the variable in the condition statement is an integer
|
||||
if !cx.typeck_results().expr_ty(cond_var).is_integral() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the variable name
|
||||
let var_name = ares_path.segments[0].ident.name.as_str();
|
||||
match cond_num_val.kind {
|
||||
ExprKind::Lit(cond_lit) => {
|
||||
// Check if the constant is zero
|
||||
if let LitKind::Int(Pu128(0), _) = cond_lit.node {
|
||||
if cx.typeck_results().expr_ty(cond_left).is_signed() {
|
||||
} else {
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
ExprKind::Call(func, []) => {
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
|
||||
&& name.ident.as_str() == "min_value"
|
||||
&& let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(func_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||
if name.ident.as_str() == "MIN"
|
||||
&& let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(const_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
ExprKind::Call(func, []) => {
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
|
||||
&& name.ident.as_str() == "min_value"
|
||||
&& let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(func_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -287,6 +287,7 @@ mod pass_by_ref_or_value;
|
|||
mod pathbuf_init_then_push;
|
||||
mod pattern_type_mismatch;
|
||||
mod permissions_set_readonly_false;
|
||||
mod pointers_in_nomem_asm_block;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_offset_with_cast;
|
||||
|
@ -386,6 +387,7 @@ mod write;
|
|||
mod zero_div_zero;
|
||||
mod zero_repeat_side_effects;
|
||||
mod zero_sized_map_values;
|
||||
mod zombie_processes;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
use clippy_config::{get_configuration_metadata, Conf};
|
||||
|
@ -623,7 +625,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
|
||||
store.register_late_pass(|_| Box::new(strings::StringAdd));
|
||||
store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
|
||||
store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
|
||||
store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
|
||||
store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
|
||||
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||
|
@ -933,5 +935,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
|
||||
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
|
||||
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
|
||||
store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
|
||||
store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
|
|
@ -77,9 +77,10 @@ impl Num {
|
|||
impl LateLintPass<'_> for ManualRangePatterns {
|
||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
|
||||
// a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
|
||||
// or at least one range
|
||||
// or more then one range (exclude triggering on stylistic using OR with one element
|
||||
// like described https://github.com/rust-lang/rust-clippy/issues/11825)
|
||||
if let PatKind::Or(pats) = pat.kind
|
||||
&& (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
|
||||
&& (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))))
|
||||
&& !in_external_macro(cx.sess(), pat.span)
|
||||
{
|
||||
let mut min = Num::dummy(i128::MAX);
|
||||
|
|
|
@ -14,7 +14,6 @@ use rustc_span::sym;
|
|||
/// - `hashmap.into_iter().map(|(_, v)| v)`
|
||||
///
|
||||
/// on `HashMaps` and `BTreeMaps` in std
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
map_type: &'tcx str, // iter / into_iter
|
||||
|
|
|
@ -441,6 +441,17 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # struct X;
|
||||
/// impl X {
|
||||
/// fn as_str(&self) -> &'static str {
|
||||
/// // ..
|
||||
/// # ""
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRONG_SELF_CONVENTION,
|
||||
style,
|
||||
|
@ -3964,7 +3975,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// let _ = 0;
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub UNNECESSARY_MIN_OR_MAX,
|
||||
complexity,
|
||||
"using 'min()/max()' when there is no need for it"
|
||||
|
@ -4025,7 +4036,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MANUAL_C_STR_LITERALS,
|
||||
pedantic,
|
||||
complexity,
|
||||
r#"creating a `CStr` through functions when `c""` literals can be used"#
|
||||
}
|
||||
|
||||
|
@ -4099,7 +4110,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// "foo".is_ascii();
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub NEEDLESS_CHARACTER_ITERATION,
|
||||
suspicious,
|
||||
"is_ascii() called on a char iterator"
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use super::UNNECESSARY_MIN_OR_MAX;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
@ -21,26 +20,30 @@ pub(super) fn check<'tcx>(
|
|||
) {
|
||||
let typeck_results = cx.typeck_results();
|
||||
let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
|
||||
if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
|
||||
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
|
||||
if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id)
|
||||
&& (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id))
|
||||
{
|
||||
let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
|
||||
return;
|
||||
};
|
||||
if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
|
||||
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
|
||||
{
|
||||
let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
|
||||
return;
|
||||
};
|
||||
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
} else if let Some(extrema) = detect_extrema(cx, recv) {
|
||||
let ord = match extrema {
|
||||
Extrema::Minimum => Ordering::Less,
|
||||
Extrema::Maximum => Ordering::Greater,
|
||||
};
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
} else if let Some(extrema) = detect_extrema(cx, arg) {
|
||||
let ord = match extrema {
|
||||
Extrema::Minimum => Ordering::Greater,
|
||||
Extrema::Maximum => Ordering::Less,
|
||||
};
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
} else if let Some(extrema) = detect_extrema(cx, recv) {
|
||||
let ord = match extrema {
|
||||
Extrema::Minimum => Ordering::Less,
|
||||
Extrema::Maximum => Ordering::Greater,
|
||||
};
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
} else if let Some(extrema) = detect_extrema(cx, arg) {
|
||||
let ord = match extrema {
|
||||
Extrema::Minimum => Ordering::Greater,
|
||||
Extrema::Maximum => Ordering::Less,
|
||||
};
|
||||
lint(cx, expr, name, recv.span, arg.span, ord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
@ -7,6 +7,7 @@ use clippy_utils::{
|
|||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
|
||||
|
@ -80,6 +81,45 @@ declare_clippy_lint! {
|
|||
"using a binding which is prefixed with an underscore"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the use of item with a single leading
|
||||
/// underscore.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A single leading underscore is usually used to indicate
|
||||
/// that a item will not be used. Using such a item breaks this
|
||||
/// expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn _foo() {}
|
||||
///
|
||||
/// struct _FooStruct {}
|
||||
///
|
||||
/// fn main() {
|
||||
/// _foo();
|
||||
/// let _ = _FooStruct{};
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo() {}
|
||||
///
|
||||
/// struct FooStruct {}
|
||||
///
|
||||
/// fn main() {
|
||||
/// foo();
|
||||
/// let _ = FooStruct{};
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USED_UNDERSCORE_ITEMS,
|
||||
pedantic,
|
||||
"using a item which is prefixed with an underscore"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the use of short circuit boolean conditions as
|
||||
|
@ -104,6 +144,7 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(LintPass => [
|
||||
TOPLEVEL_REF_ARG,
|
||||
USED_UNDERSCORE_BINDING,
|
||||
USED_UNDERSCORE_ITEMS,
|
||||
SHORT_CIRCUIT_STATEMENT,
|
||||
]);
|
||||
|
||||
|
@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
{
|
||||
return;
|
||||
}
|
||||
let (definition_hir_id, ident) = match expr.kind {
|
||||
ExprKind::Path(ref qpath) => {
|
||||
if let QPath::Resolved(None, path) = qpath
|
||||
&& let Res::Local(id) = path.res
|
||||
&& is_used(cx, expr)
|
||||
{
|
||||
(id, last_path_segment(qpath).ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Field(recv, ident) => {
|
||||
if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
|
||||
&& let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
|
||||
&& let Some(local_did) = field.did.as_local()
|
||||
&& !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
|
||||
{
|
||||
(cx.tcx.local_def_id_to_hir_id(local_did), ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let name = ident.name.as_str();
|
||||
if name.starts_with('_')
|
||||
&& !name.starts_with("__")
|
||||
&& let definition_span = cx.tcx.hir().span(definition_hir_id)
|
||||
&& !definition_span.from_expansion()
|
||||
&& !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USED_UNDERSCORE_BINDING,
|
||||
expr.span,
|
||||
format!(
|
||||
"used binding `{name}` which is prefixed with an underscore. A leading \
|
||||
underscore signals that a binding will not be used"
|
||||
),
|
||||
|diag| {
|
||||
diag.span_note(definition_span, format!("`{name}` is defined here"));
|
||||
},
|
||||
);
|
||||
}
|
||||
used_underscore_binding(cx, expr);
|
||||
used_underscore_items(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (def_id, ident) = match expr.kind {
|
||||
ExprKind::Call(func, ..) => {
|
||||
if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind
|
||||
&& let Some(last_segment) = path.segments.last()
|
||||
&& let Res::Def(_, def_id) = last_segment.res
|
||||
{
|
||||
(def_id, last_segment.ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(path, ..) => {
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
(def_id, path.ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
|
||||
if let Some(last_segment) = path.segments.last()
|
||||
&& let Res::Def(_, def_id) = last_segment.res
|
||||
{
|
||||
(def_id, last_segment.ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let name = ident.name.as_str();
|
||||
let definition_span = cx.tcx.def_span(def_id);
|
||||
if name.starts_with('_')
|
||||
&& !name.starts_with("__")
|
||||
&& !definition_span.from_expansion()
|
||||
&& def_id.krate == LOCAL_CRATE
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USED_UNDERSCORE_ITEMS,
|
||||
expr.span,
|
||||
"used underscore-prefixed item".to_string(),
|
||||
|diag| {
|
||||
diag.span_note(definition_span, "item is defined here".to_string());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (definition_hir_id, ident) = match expr.kind {
|
||||
ExprKind::Path(ref qpath) => {
|
||||
if let QPath::Resolved(None, path) = qpath
|
||||
&& let Res::Local(id) = path.res
|
||||
&& is_used(cx, expr)
|
||||
{
|
||||
(id, last_path_segment(qpath).ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Field(recv, ident) => {
|
||||
if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
|
||||
&& let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
|
||||
&& let Some(local_did) = field.did.as_local()
|
||||
&& !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
|
||||
{
|
||||
(cx.tcx.local_def_id_to_hir_id(local_did), ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let name = ident.name.as_str();
|
||||
if name.starts_with('_')
|
||||
&& !name.starts_with("__")
|
||||
&& let definition_span = cx.tcx.hir().span(definition_hir_id)
|
||||
&& !definition_span.from_expansion()
|
||||
&& !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USED_UNDERSCORE_BINDING,
|
||||
expr.span,
|
||||
"used underscore-prefixed binding".to_string(),
|
||||
|diag| {
|
||||
diag.span_note(definition_span, "binding is defined here".to_string());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind};
|
||||
|
@ -134,6 +135,11 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, e) {
|
||||
if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
|
||||
return;
|
||||
}
|
||||
}
|
||||
span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -26,7 +26,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// // or choose alternative bounds for `T` so that it can be unsized
|
||||
/// ```
|
||||
#[clippy::version = "1.79.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub NEEDLESS_MAYBE_SIZED,
|
||||
suspicious,
|
||||
"a `?Sized` bound that is unusable due to a `Sized` requirement"
|
||||
|
|
|
@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Collect moved variables and spans which will need dereferencings from the
|
||||
// Collect moved variables and spans which will need dereferencing from the
|
||||
// function body.
|
||||
let MovedVariablesCtxt { moved_vars } = {
|
||||
let mut ctx = MovedVariablesCtxt::default();
|
||||
|
@ -148,12 +148,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
return;
|
||||
}
|
||||
|
||||
// Ignore `self`s.
|
||||
if idx == 0 {
|
||||
if let PatKind::Binding(.., ident, _) = arg.pat.kind {
|
||||
if ident.name == kw::SelfLower {
|
||||
continue;
|
||||
}
|
||||
// Ignore `self`s and params whose variable name starts with an underscore
|
||||
if let PatKind::Binding(.., ident, _) = arg.pat.kind {
|
||||
if idx == 0 && ident.name == kw::SelfLower {
|
||||
continue;
|
||||
}
|
||||
if ident.name.as_str().starts_with('_') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use rustc_hir::Expr;
|
||||
use clippy_utils::{is_in_test, match_def_path, paths};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
|
@ -95,10 +96,49 @@ impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC])
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
if is_panic(cx, macro_call.def_id) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
|
||||
if is_panic(cx, macro_call.def_id) {
|
||||
if cx.tcx.hir().is_inside_const_context(expr.hir_id)
|
||||
|| self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint(
|
||||
cx,
|
||||
PANIC,
|
||||
macro_call.span,
|
||||
"`panic` should not be present in production code",
|
||||
);
|
||||
return;
|
||||
}
|
||||
match cx.tcx.item_name(macro_call.def_id).as_str() {
|
||||
"todo" => {
|
||||
span_lint(
|
||||
cx,
|
||||
TODO,
|
||||
macro_call.span,
|
||||
"`todo` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unimplemented" => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIMPLEMENTED,
|
||||
macro_call.span,
|
||||
"`unimplemented` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unreachable" => {
|
||||
span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else if let ExprKind::Call(func, [_]) = expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind
|
||||
&& let Res::Def(DefKind::Fn, def_id) = expr_path.res
|
||||
&& match_def_path(cx, def_id, &paths::PANIC_ANY)
|
||||
{
|
||||
if cx.tcx.hir().is_inside_const_context(expr.hir_id)
|
||||
|| self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
|
||||
{
|
||||
|
@ -108,32 +148,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
span_lint(
|
||||
cx,
|
||||
PANIC,
|
||||
macro_call.span,
|
||||
"`panic` should not be present in production code",
|
||||
expr.span,
|
||||
"`panic_any` should not be present in production code",
|
||||
);
|
||||
return;
|
||||
}
|
||||
match cx.tcx.item_name(macro_call.def_id).as_str() {
|
||||
"todo" => {
|
||||
span_lint(
|
||||
cx,
|
||||
TODO,
|
||||
macro_call.span,
|
||||
"`todo` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unimplemented" => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIMPLEMENTED,
|
||||
macro_call.span,
|
||||
"`unimplemented` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unreachable" => {
|
||||
span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
88
clippy_lints/src/pointers_in_nomem_asm_block.rs
Normal file
88
clippy_lints/src/pointers_in_nomem_asm_block.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::InlineAsmOptions;
|
||||
use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if any pointer is being passed to an asm! block with `nomem` option.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `nomem` forbids any reads or writes to memory and passing a pointer suggests
|
||||
/// that either of those will happen.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn f(p: *mut u32) {
|
||||
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn f(p: *mut u32) {
|
||||
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub POINTERS_IN_NOMEM_ASM_BLOCK,
|
||||
suspicious,
|
||||
"pointers in nomem asm block"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::InlineAsm(asm) = &expr.kind {
|
||||
check_asm(cx, asm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) {
|
||||
if !asm.options.contains(InlineAsmOptions::NOMEM) {
|
||||
return;
|
||||
}
|
||||
|
||||
let spans = asm
|
||||
.operands
|
||||
.iter()
|
||||
.filter(|(op, _span)| has_in_operand_pointer(cx, op))
|
||||
.map(|(_op, span)| *span)
|
||||
.collect::<Vec<Span>>();
|
||||
|
||||
if spans.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
POINTERS_IN_NOMEM_ASM_BLOCK,
|
||||
spans,
|
||||
"passing pointers to nomem asm block",
|
||||
additional_notes,
|
||||
);
|
||||
}
|
||||
|
||||
fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool {
|
||||
let asm_in_expr = match asm_op {
|
||||
InlineAsmOperand::SymStatic { .. }
|
||||
| InlineAsmOperand::Out { .. }
|
||||
| InlineAsmOperand::Const { .. }
|
||||
| InlineAsmOperand::SymFn { .. }
|
||||
| InlineAsmOperand::Label { .. } => return false,
|
||||
InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr,
|
||||
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr,
|
||||
};
|
||||
|
||||
// This checks for raw ptrs, refs and function pointers - the last one
|
||||
// also technically counts as reading memory.
|
||||
cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr()
|
||||
}
|
||||
|
||||
fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) {
|
||||
diag.note("`nomem` means that no memory write or read happens inside the asm! block");
|
||||
diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint");
|
||||
}
|
|
@ -42,7 +42,7 @@ declare_clippy_lint! {
|
|||
/// println!("inserted {value:?}");
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub SET_CONTAINS_OR_INSERT,
|
||||
nursery,
|
||||
"call to `<set>::contains` followed by `<set>::insert`"
|
||||
|
|
|
@ -33,7 +33,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// "Hello World!".trim_end_matches(['.', ',', '!', '?']);
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
#[clippy::version = "1.81.0"]
|
||||
pub MANUAL_PATTERN_CHAR_COMPARISON,
|
||||
style,
|
||||
"manual char comparison in string patterns"
|
||||
|
|
334
clippy_lints/src/zombie_processes.rs
Normal file
334
clippy_lints/src/zombie_processes.rs
Normal file
|
@ -0,0 +1,334 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_block, walk_expr, walk_local, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use std::ops::ControlFlow;
|
||||
use ControlFlow::{Break, Continue};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for code that spawns a process but never calls `wait()` on the child.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning),
|
||||
/// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process.
|
||||
/// Not doing so will effectively leak process IDs and/or other limited global resources,
|
||||
/// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications.
|
||||
/// Such processes are called "zombie processes".
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let _child = Command::new("ls").spawn().expect("failed to execute child");
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::process::Command;
|
||||
///
|
||||
/// let mut child = Command::new("ls").spawn().expect("failed to execute child");
|
||||
/// child.wait().expect("failed to wait on child");
|
||||
/// ```
|
||||
#[clippy::version = "1.74.0"]
|
||||
pub ZOMBIE_PROCESSES,
|
||||
suspicious,
|
||||
"not waiting on a spawned child process"
|
||||
}
|
||||
declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind
|
||||
&& let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def()
|
||||
&& match_def_path(cx, child_adt.did(), &paths::CHILD)
|
||||
{
|
||||
match cx.tcx.parent_hir_node(expr.hir_id) {
|
||||
Node::LetStmt(local)
|
||||
if let PatKind::Binding(_, local_id, ..) = local.pat.kind
|
||||
&& let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) =>
|
||||
{
|
||||
let mut vis = WaitFinder::WalkUpTo(cx, local_id);
|
||||
|
||||
// If it does have a `wait()` call, we're done. Don't lint.
|
||||
if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't emit a suggestion since the binding is used later
|
||||
check(cx, expr, false);
|
||||
},
|
||||
Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => {
|
||||
// `let _ = child;`, also dropped immediately without `wait()`ing
|
||||
check(cx, expr, true);
|
||||
},
|
||||
Node::Stmt(&Stmt {
|
||||
kind: StmtKind::Semi(_),
|
||||
..
|
||||
}) => {
|
||||
// Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();`
|
||||
check(cx, expr, true);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum BreakReason {
|
||||
WaitFound,
|
||||
EarlyReturn,
|
||||
}
|
||||
|
||||
/// A visitor responsible for finding a `wait()` call on a local variable.
|
||||
///
|
||||
/// Conditional `wait()` calls are assumed to not call wait:
|
||||
/// ```ignore
|
||||
/// let mut c = Command::new("").spawn().unwrap();
|
||||
/// if true {
|
||||
/// c.wait();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the
|
||||
/// inverse -- checking if all uses of the local are either:
|
||||
/// - a field access (`child.{stderr,stdin,stdout}`)
|
||||
/// - calling `id` or `kill`
|
||||
/// - no use at all (e.g. `let _x = child;`)
|
||||
/// - taking a shared reference (`&`), `wait()` can't go through that
|
||||
///
|
||||
/// None of these are sufficient to prevent zombie processes.
|
||||
/// Doing it like this means more FNs, but FNs are better than FPs.
|
||||
///
|
||||
/// `return` expressions, conditional or not, short-circuit the visitor because
|
||||
/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point:
|
||||
/// ```ignore
|
||||
/// let mut c = Command::new("").spawn().unwrap();
|
||||
/// if true {
|
||||
/// return; // Break(BreakReason::EarlyReturn)
|
||||
/// }
|
||||
/// c.wait(); // this might not be reachable
|
||||
/// ```
|
||||
enum WaitFinder<'a, 'tcx> {
|
||||
WalkUpTo(&'a LateContext<'tcx>, HirId),
|
||||
Found(&'a LateContext<'tcx>, HirId),
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> {
|
||||
type Result = ControlFlow<BreakReason>;
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
|
||||
if let Self::WalkUpTo(cx, local_id) = *self
|
||||
&& let PatKind::Binding(_, pat_id, ..) = l.pat.kind
|
||||
&& local_id == pat_id
|
||||
{
|
||||
*self = Self::Found(cx, local_id);
|
||||
}
|
||||
|
||||
walk_local(self, l)
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
|
||||
let Self::Found(cx, local_id) = *self else {
|
||||
return walk_expr(self, ex);
|
||||
};
|
||||
|
||||
if path_to_local_id(ex, local_id) {
|
||||
match cx.tcx.parent_hir_node(ex.hir_id) {
|
||||
Node::Stmt(Stmt {
|
||||
kind: StmtKind::Semi(_),
|
||||
..
|
||||
}) => {},
|
||||
Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {},
|
||||
Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {},
|
||||
Node::Expr(expr)
|
||||
if let Some(fn_did) = fn_def_id(cx, expr)
|
||||
&& match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {},
|
||||
|
||||
// Conservatively assume that all other kinds of nodes call `.wait()` somehow.
|
||||
_ => return Break(BreakReason::WaitFound),
|
||||
}
|
||||
} else {
|
||||
match ex.kind {
|
||||
ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn),
|
||||
ExprKind::If(cond, then, None) => {
|
||||
walk_expr(self, cond)?;
|
||||
|
||||
// A `wait()` call in an if expression with no else is not enough:
|
||||
//
|
||||
// let c = spawn();
|
||||
// if true {
|
||||
// c.wait();
|
||||
// }
|
||||
//
|
||||
// This might not call wait(). However, early returns are propagated,
|
||||
// because they might lead to a later wait() not being called.
|
||||
if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) {
|
||||
return Break(BreakReason::EarlyReturn);
|
||||
}
|
||||
|
||||
return Continue(());
|
||||
},
|
||||
|
||||
ExprKind::If(cond, then, Some(else_)) => {
|
||||
walk_expr(self, cond)?;
|
||||
|
||||
#[expect(clippy::unnested_or_patterns)]
|
||||
match (walk_expr(self, then), walk_expr(self, else_)) {
|
||||
(Continue(()), Continue(()))
|
||||
|
||||
// `wait()` in one branch but nothing in the other does not count
|
||||
| (Continue(()), Break(BreakReason::WaitFound))
|
||||
| (Break(BreakReason::WaitFound), Continue(())) => {},
|
||||
|
||||
// `wait()` in both branches is ok
|
||||
(Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => {
|
||||
return Break(BreakReason::WaitFound);
|
||||
},
|
||||
|
||||
// Propagate early returns in either branch
|
||||
(Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => {
|
||||
return Break(BreakReason::EarlyReturn);
|
||||
},
|
||||
}
|
||||
|
||||
return Continue(());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
walk_expr(self, ex)
|
||||
}
|
||||
}
|
||||
|
||||
/// This function has shared logic between the different kinds of nodes that can trigger the lint.
|
||||
///
|
||||
/// In particular, `let <binding> = <expr that spawns child>;` requires some custom additional logic
|
||||
/// such as checking that the binding is not used in certain ways, which isn't necessary for
|
||||
/// `let _ = <expr that spawns child>;`.
|
||||
///
|
||||
/// This checks if the program doesn't unconditionally exit after the spawn expression.
|
||||
fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) {
|
||||
let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut vis = ExitPointFinder {
|
||||
cx,
|
||||
state: ExitPointState::WalkUpTo(spawn_expr.hir_id),
|
||||
};
|
||||
if let Break(ExitCallFound) = vis.visit_block(block) {
|
||||
// Visitor found an unconditional `exit()` call, so don't lint.
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ZOMBIE_PROCESSES,
|
||||
spawn_expr.span,
|
||||
"spawned process is never `wait()`ed on",
|
||||
|diag| {
|
||||
if emit_suggestion {
|
||||
diag.span_suggestion(
|
||||
spawn_expr.span.shrink_to_hi(),
|
||||
"try",
|
||||
".wait()",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
diag.note("consider calling `.wait()`");
|
||||
}
|
||||
|
||||
diag.note("not doing so might leave behind zombie processes")
|
||||
.note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if the given expression exits the process.
|
||||
fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
fn_def_id(cx, expr).is_some_and(|fn_did| {
|
||||
cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ExitPointState {
|
||||
/// Still walking up to the expression that initiated the visitor.
|
||||
WalkUpTo(HirId),
|
||||
/// We're inside of a control flow construct (e.g. `if`, `match`, `loop`)
|
||||
/// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these
|
||||
/// constructs later and still continue looking for an `exit()` call afterwards. Example:
|
||||
/// ```ignore
|
||||
/// Command::new("").spawn().unwrap();
|
||||
///
|
||||
/// if true { // depth=1
|
||||
/// if true { // depth=2
|
||||
/// match () { // depth=3
|
||||
/// () => loop { // depth=4
|
||||
///
|
||||
/// std::process::exit();
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored
|
||||
///
|
||||
/// } // depth=3
|
||||
/// } // depth=2
|
||||
/// } // depth=1
|
||||
/// } // depth=0
|
||||
///
|
||||
/// std::process::exit();
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it
|
||||
/// ```
|
||||
/// We can only get into this state from `NoExit`.
|
||||
InControlFlow { depth: u32 },
|
||||
/// No exit call found yet, but looking for one.
|
||||
NoExit,
|
||||
}
|
||||
|
||||
fn expr_enters_control_flow(expr: &Expr<'_>) -> bool {
|
||||
matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..))
|
||||
}
|
||||
|
||||
struct ExitPointFinder<'a, 'tcx> {
|
||||
state: ExitPointState,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
struct ExitCallFound;
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> {
|
||||
type Result = ControlFlow<ExitCallFound>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
|
||||
match self.state {
|
||||
ExitPointState::WalkUpTo(id) if expr.hir_id == id => {
|
||||
self.state = ExitPointState::NoExit;
|
||||
walk_expr(self, expr)
|
||||
},
|
||||
ExitPointState::NoExit if expr_enters_control_flow(expr) => {
|
||||
self.state = ExitPointState::InControlFlow { depth: 1 };
|
||||
walk_expr(self, expr)?;
|
||||
if let ExitPointState::InControlFlow { .. } = self.state {
|
||||
self.state = ExitPointState::NoExit;
|
||||
}
|
||||
Continue(())
|
||||
},
|
||||
ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound),
|
||||
ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => {
|
||||
*depth += 1;
|
||||
walk_expr(self, expr)?;
|
||||
match self.state {
|
||||
ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit,
|
||||
ExitPointState::InControlFlow { ref mut depth } => *depth -= 1,
|
||||
_ => {},
|
||||
}
|
||||
Continue(())
|
||||
},
|
||||
_ => Continue(()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -183,15 +183,15 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
|
|||
let mut iter = tokenize_with_text(src);
|
||||
|
||||
// Search for the token sequence [`#`, `[`, `cfg`]
|
||||
while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
|
||||
let mut iter = iter.by_ref().skip_while(|(t, _)| {
|
||||
while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) {
|
||||
let mut iter = iter.by_ref().skip_while(|(t, ..)| {
|
||||
matches!(
|
||||
t,
|
||||
TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
|
||||
)
|
||||
});
|
||||
if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
|
||||
&& matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
|
||||
if matches!(iter.next(), Some((TokenKind::OpenBracket, ..)))
|
||||
&& matches!(iter.next(), Some((TokenKind::Ident, "cfg", _)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1194,8 +1194,8 @@ fn eq_span_tokens(
|
|||
&& let Some(rsrc) = right.get_source_range(cx)
|
||||
&& let Some(rsrc) = rsrc.as_str()
|
||||
{
|
||||
let pred = |t: &(_, _)| pred(t.0);
|
||||
let map = |(_, x)| x;
|
||||
let pred = |&(token, ..): &(TokenKind, _, _)| pred(token);
|
||||
let map = |(_, source, _)| source;
|
||||
|
||||
let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
|
||||
let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
|
||||
|
|
|
@ -121,7 +121,7 @@ use rustc_middle::ty::{
|
|||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{sym, InnerSpan, Span};
|
||||
use rustc_target::abi::Integer;
|
||||
use visitors::Visitable;
|
||||
|
||||
|
@ -2950,13 +2950,14 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
|
|||
}
|
||||
|
||||
/// Tokenizes the input while keeping the text associated with each token.
|
||||
pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str)> {
|
||||
pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
|
||||
let mut pos = 0;
|
||||
tokenize(s).map(move |t| {
|
||||
let end = pos + t.len;
|
||||
let range = pos as usize..end as usize;
|
||||
let inner = InnerSpan::new(range.start, range.end);
|
||||
pos = end;
|
||||
(t.kind, s.get(range).unwrap_or_default())
|
||||
(t.kind, s.get(range).unwrap_or_default(), inner)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2980,8 +2981,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
|
|||
pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
|
||||
let snippet = sm.span_to_snippet(span).unwrap_or_default();
|
||||
let res = tokenize_with_text(&snippet)
|
||||
.filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
|
||||
.map(|(_, s)| s)
|
||||
.filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
|
||||
.map(|(_, s, _)| s)
|
||||
.join("\n");
|
||||
res
|
||||
}
|
||||
|
@ -3001,7 +3002,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
|
|||
/// pat: Some(a)
|
||||
/// else_body: return None
|
||||
/// ```
|
||||
|
||||
///
|
||||
/// And for this example:
|
||||
/// ```ignore
|
||||
/// let Some(FooBar { a, b }) = ex else { return None };
|
||||
|
@ -3011,7 +3012,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
|
|||
/// pat: Some(FooBar { a, b })
|
||||
/// else_body: return None
|
||||
/// ```
|
||||
|
||||
///
|
||||
/// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because
|
||||
/// the question mark operator is applicable here. Callers have to check whether we are in a
|
||||
/// constant or not.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! Whenever possible, please consider diagnostic items over hardcoded paths.
|
||||
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
|
||||
|
||||
pub const ABORT: [&str; 3] = ["std", "process", "abort"];
|
||||
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
|
||||
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
|
||||
["rustc_lint_defs", "Applicability", "Unspecified"],
|
||||
|
@ -23,6 +24,9 @@ pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
|
|||
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
|
||||
pub const CHILD: [&str; 3] = ["std", "process", "Child"];
|
||||
pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"];
|
||||
pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
|
||||
pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"];
|
||||
|
@ -56,6 +60,7 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to
|
|||
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
|
||||
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
|
||||
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
|
||||
pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
|
||||
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
|
||||
pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
|
||||
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
|
||||
|
|
|
@ -666,39 +666,6 @@ pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
|
|||
(outer_span.ctxt() == outer).then_some(outer_span)
|
||||
}
|
||||
|
||||
/// Removes block comments from the given `Vec` of lines.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// without_block_comments(vec!["/*", "foo", "*/"]);
|
||||
/// // => vec![]
|
||||
///
|
||||
/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
|
||||
/// // => vec!["bar"]
|
||||
/// ```
|
||||
pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
|
||||
let mut without = vec![];
|
||||
|
||||
let mut nest_level = 0;
|
||||
|
||||
for line in lines {
|
||||
if line.contains("/*") {
|
||||
nest_level += 1;
|
||||
continue;
|
||||
} else if line.contains("*/") {
|
||||
nest_level -= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if nest_level == 0 {
|
||||
without.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
without
|
||||
}
|
||||
|
||||
/// Trims the whitespace from the start and the end of the span.
|
||||
pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
|
||||
let data = span.data();
|
||||
|
@ -776,7 +743,7 @@ pub fn str_literal_to_char_literal(
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{reindent_multiline, without_block_comments};
|
||||
use super::reindent_multiline;
|
||||
|
||||
#[test]
|
||||
fn test_reindent_multiline_single_line() {
|
||||
|
@ -844,29 +811,4 @@ mod test {
|
|||
z
|
||||
}".into(), true, Some(8)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_without_block_comments_lines_without_block_comments() {
|
||||
let result = without_block_comments(vec!["/*", "", "*/"]);
|
||||
println!("result: {result:?}");
|
||||
assert!(result.is_empty());
|
||||
|
||||
let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
|
||||
assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
|
||||
|
||||
let result = without_block_comments(vec!["/* rust", "", "*/"]);
|
||||
assert!(result.is_empty());
|
||||
|
||||
let result = without_block_comments(vec!["/* one-line comment */"]);
|
||||
assert!(result.is_empty());
|
||||
|
||||
let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
|
||||
assert!(result.is_empty());
|
||||
|
||||
let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
|
||||
assert!(result.is_empty());
|
||||
|
||||
let result = without_block_comments(vec!["foo", "bar", "baz"]);
|
||||
assert_eq!(result, vec!["foo", "bar", "baz"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,8 +180,10 @@ impl<'a> Sugg<'a> {
|
|||
) -> Self {
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
|
||||
let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0;
|
||||
|
||||
match expr.kind {
|
||||
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)),
|
||||
ast::ExprKind::AddrOf(..)
|
||||
| ast::ExprKind::Closure { .. }
|
||||
| ast::ExprKind::If(..)
|
||||
|
@ -224,46 +226,38 @@ impl<'a> Sugg<'a> {
|
|||
| ast::ExprKind::While(..)
|
||||
| ast::ExprKind::Await(..)
|
||||
| ast::ExprKind::Err(_)
|
||||
| ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
| ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
||||
AssocOp::DotDot,
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
|
||||
rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
|
||||
),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
|
||||
AssocOp::DotDotEq,
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
|
||||
rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
|
||||
),
|
||||
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
|
||||
AssocOp::Assign,
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
snippet(lhs.span),
|
||||
snippet(rhs.span),
|
||||
),
|
||||
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
astbinop2assignop(op),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
snippet(lhs.span),
|
||||
snippet(rhs.span),
|
||||
),
|
||||
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
AssocOp::from_ast_binop(op.node),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
snippet(lhs.span),
|
||||
snippet(rhs.span),
|
||||
),
|
||||
ast::ExprKind::Cast(ref lhs, ref ty) |
|
||||
//FIXME(chenyukang), remove this after type ascription is removed from AST
|
||||
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
|
||||
AssocOp::As,
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||
snippet(lhs.span),
|
||||
snippet(ty.span),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -268,8 +268,6 @@ pub fn main() {
|
|||
},
|
||||
_ => Some(s.to_string()),
|
||||
})
|
||||
// FIXME: remove this line in 1.79 to only keep `--cfg clippy`.
|
||||
.chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
|
||||
.chain(vec!["--cfg".into(), "clippy".into()])
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
|
|||
error: hardcoded path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
|
||||
|
|
||||
LL | const OPS_MOD: [&str; 5] = ["core", "ops"];
|
||||
| ^^^^^^^^^^^^^^^
|
||||
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `sym::deref_method`
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[warn(clippy::disallowed_names)]
|
||||
#![warn(clippy::disallowed_names)]
|
||||
|
||||
fn main() {
|
||||
// `foo` is part of the default configuration
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[warn(clippy::disallowed_names)]
|
||||
#![warn(clippy::disallowed_names)]
|
||||
|
||||
fn main() {
|
||||
// `foo` is part of the default configuration
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//@compile-flags: --test
|
||||
#![warn(clippy::panic)]
|
||||
use std::panic::panic_any;
|
||||
|
||||
fn main() {
|
||||
enum Enam {
|
||||
|
@ -12,6 +13,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn issue_13292() {
|
||||
panic_any("should lint")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lonely_test() {
|
||||
enum Enam {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: `panic` should not be present in production code
|
||||
--> tests/ui-toml/panic/panic.rs:11:14
|
||||
--> tests/ui-toml/panic/panic.rs:12:14
|
||||
|
|
||||
LL | _ => panic!(""),
|
||||
| ^^^^^^^^^^
|
||||
|
@ -7,5 +7,11 @@ LL | _ => panic!(""),
|
|||
= note: `-D clippy::panic` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::panic)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: `panic_any` should not be present in production code
|
||||
--> tests/ui-toml/panic/panic.rs:17:5
|
||||
|
|
||||
LL | panic_any("should lint")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ use proc_macros::{external, with_span};
|
|||
#[warn(deref_nullptr)]
|
||||
#[deny(deref_nullptr)]
|
||||
#[forbid(deref_nullptr)]
|
||||
|
||||
fn main() {
|
||||
external! {
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -36,7 +36,7 @@ LL | #[expect(dead_code)]
|
|||
= help: try adding a reason at the end with `, reason = ".."`
|
||||
|
||||
error: `allow` attribute without specifying a reason
|
||||
--> tests/ui/allow_attributes_without_reason.rs:47:5
|
||||
--> tests/ui/allow_attributes_without_reason.rs:46:5
|
||||
|
|
||||
LL | #[allow(unused)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -44,7 +44,7 @@ LL | #[allow(unused)]
|
|||
= help: try adding a reason at the end with `, reason = ".."`
|
||||
|
||||
error: `allow` attribute without specifying a reason
|
||||
--> tests/ui/allow_attributes_without_reason.rs:47:5
|
||||
--> tests/ui/allow_attributes_without_reason.rs:46:5
|
||||
|
|
||||
LL | #[allow(unused)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
7
tests/ui/auxiliary/external_item.rs
Normal file
7
tests/ui/auxiliary/external_item.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub struct _ExternalStruct {}
|
||||
|
||||
impl _ExternalStruct {
|
||||
pub fn _foo(self) {}
|
||||
}
|
||||
|
||||
pub fn _exernal_foo() {}
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
#![feature(rustc_private)]
|
||||
#![feature(core_intrinsics)]
|
||||
extern crate libc;
|
||||
|
||||
#[warn(clippy::cast_ptr_alignment)]
|
||||
#[allow(
|
||||
#![warn(clippy::cast_ptr_alignment)]
|
||||
#![allow(
|
||||
clippy::no_effect,
|
||||
clippy::unnecessary_operation,
|
||||
clippy::cast_lossless,
|
||||
clippy::borrow_as_ptr
|
||||
)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
fn main() {
|
||||
/* These should be warned against */
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#[allow(clippy::unnecessary_operation)]
|
||||
#[allow(clippy::implicit_clone)]
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::implicit_clone)]
|
||||
|
||||
fn main() {
|
||||
let x = &Baz;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
|
||||
#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
#[warn(clippy::collapsible_else_if)]
|
||||
|
||||
fn main() {
|
||||
let x = "hello";
|
||||
let y = "world";
|
||||
|
@ -76,7 +74,6 @@ fn main() {
|
|||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(dead_code)]
|
||||
fn issue_7318() {
|
||||
if true { println!("I've been resolved!")
|
||||
}else if false {}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
|
||||
#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
#[warn(clippy::collapsible_else_if)]
|
||||
|
||||
fn main() {
|
||||
let x = "hello";
|
||||
let y = "world";
|
||||
|
@ -90,7 +88,6 @@ fn main() {
|
|||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(dead_code)]
|
||||
fn issue_7318() {
|
||||
if true { println!("I've been resolved!")
|
||||
}else{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:13:12
|
||||
--> tests/ui/collapsible_else_if.rs:11:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -19,7 +19,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:21:12
|
||||
--> tests/ui/collapsible_else_if.rs:19:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -37,7 +37,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:29:12
|
||||
--> tests/ui/collapsible_else_if.rs:27:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -60,7 +60,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:40:12
|
||||
--> tests/ui/collapsible_else_if.rs:38:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -83,7 +83,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:51:12
|
||||
--> tests/ui/collapsible_else_if.rs:49:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -106,7 +106,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:62:12
|
||||
--> tests/ui/collapsible_else_if.rs:60:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -129,7 +129,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:73:12
|
||||
--> tests/ui/collapsible_else_if.rs:71:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -152,7 +152,7 @@ LL + }
|
|||
|
|
||||
|
||||
error: this `else { if .. }` block can be collapsed
|
||||
--> tests/ui/collapsible_else_if.rs:96:10
|
||||
--> tests/ui/collapsible_else_if.rs:93:10
|
||||
|
|
||||
LL | }else{
|
||||
| __________^
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/1698
|
||||
|
||||
pub trait Trait {
|
||||
const CONSTANT: u8;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#[allow(dead_code)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/478
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/478
|
||||
|
||||
enum Baz {
|
||||
One,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/11230
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/11230
|
||||
|
||||
fn main() {
|
||||
const A: &[for<'a> fn(&'a ())] = &[];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::all)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/1588
|
||||
|
||||
fn main() {
|
||||
match 1 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::all)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/1969
|
||||
|
||||
fn main() {}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
|
||||
|
||||
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
|
||||
///
|
||||
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
|
||||
// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
|
||||
//
|
||||
// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
|
||||
|
||||
fn f(s: &[u8]) -> bool {
|
||||
let t = s[0] as char;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
|
||||
///
|
||||
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
|
||||
|
||||
fn spanless_hash_ice() {
|
||||
let txt = "something";
|
||||
let empty_header: [u8; 1] = [1; 1];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/2727
|
||||
|
||||
pub fn f(new: fn()) {
|
||||
new();
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
dead_code
|
||||
)]
|
||||
|
||||
/// This should not compile-fail with:
|
||||
///
|
||||
/// error[E0277]: the trait bound `T: Foo` is not satisfied
|
||||
// See rust-lang/rust-clippy#2760.
|
||||
// This should not compile-fail with:
|
||||
//
|
||||
// error[E0277]: the trait bound `T: Foo` is not satisfied
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/2760
|
||||
|
||||
trait Foo {
|
||||
type Bar;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/2862
|
||||
|
||||
pub trait FooMap {
|
||||
fn map<B, F: Fn() -> B>(&self, f: F) -> B;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(dead_code, clippy::extra_unused_lifetimes)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/2865
|
||||
|
||||
struct Ice {
|
||||
size: String,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/3151
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HashMap<V, S> {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)]
|
||||
#![allow(unused)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/3462
|
||||
|
||||
enum Foo {
|
||||
Bar,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/3747
|
||||
|
||||
macro_rules! a {
|
||||
( $pub:tt $($attr:tt)* ) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/700
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/700
|
||||
|
||||
fn core() {}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/1336
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::comparison_chain)]
|
||||
#![deny(clippy::if_same_then_else)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/2426
|
||||
|
||||
fn main() {}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![deny(clippy::multiple_inherent_impl)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/4578
|
||||
|
||||
macro_rules! impl_foo {
|
||||
($struct:ident) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(warnings)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/825
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/825
|
||||
|
||||
// this should compile in a reasonable amount of time
|
||||
fn rust_type_id(name: &str) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![deny(clippy::match_same_arms)]
|
||||
|
||||
/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/2427
|
||||
|
||||
const PRICE_OF_SWEETS: u32 = 5;
|
||||
const PRICE_OF_KINDNESS: u32 = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
|
||||
// Test for https://github.com/rust-lang/rust-clippy/issues/1346
|
||||
|
||||
#[deny(warnings)]
|
||||
fn cfg_return() -> i32 {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
|
||||
#![warn(clippy::expl_impl_clone_on_copy)]
|
||||
|
||||
|
||||
#[derive(Copy)]
|
||||
struct Qux;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:8:1
|
||||
--> tests/ui/derive.rs:7:1
|
||||
|
|
||||
LL | / impl Clone for Qux {
|
||||
LL | |
|
||||
|
@ -10,7 +10,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:8:1
|
||||
--> tests/ui/derive.rs:7:1
|
||||
|
|
||||
LL | / impl Clone for Qux {
|
||||
LL | |
|
||||
|
@ -23,7 +23,7 @@ LL | | }
|
|||
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:33:1
|
||||
--> tests/ui/derive.rs:32:1
|
||||
|
|
||||
LL | / impl<'a> Clone for Lt<'a> {
|
||||
LL | |
|
||||
|
@ -34,7 +34,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:33:1
|
||||
--> tests/ui/derive.rs:32:1
|
||||
|
|
||||
LL | / impl<'a> Clone for Lt<'a> {
|
||||
LL | |
|
||||
|
@ -45,7 +45,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:45:1
|
||||
--> tests/ui/derive.rs:44:1
|
||||
|
|
||||
LL | / impl Clone for BigArray {
|
||||
LL | |
|
||||
|
@ -56,7 +56,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:45:1
|
||||
--> tests/ui/derive.rs:44:1
|
||||
|
|
||||
LL | / impl Clone for BigArray {
|
||||
LL | |
|
||||
|
@ -67,7 +67,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:57:1
|
||||
--> tests/ui/derive.rs:56:1
|
||||
|
|
||||
LL | / impl Clone for FnPtr {
|
||||
LL | |
|
||||
|
@ -78,7 +78,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:57:1
|
||||
--> tests/ui/derive.rs:56:1
|
||||
|
|
||||
LL | / impl Clone for FnPtr {
|
||||
LL | |
|
||||
|
@ -89,7 +89,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:78:1
|
||||
--> tests/ui/derive.rs:77:1
|
||||
|
|
||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||
LL | |
|
||||
|
@ -100,7 +100,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:78:1
|
||||
--> tests/ui/derive.rs:77:1
|
||||
|
|
||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||
LL | |
|
||||
|
|
|
@ -67,3 +67,9 @@ fn foobar() {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn ignore_todo() {
|
||||
let x: u32 = todo!();
|
||||
println!("{x}");
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// https://github.com/rust-lang/rust-clippy/issues/12917
|
||||
#![warn(clippy::doc_lazy_continuation)]
|
||||
|
||||
/// This is a constant.
|
||||
///
|
||||
/// The meaning of which should not be explained.
|
||||
pub const A: i32 = 42;
|
||||
|
||||
/// This is another constant, no longer used.
|
||||
///
|
||||
/// This block of documentation has a long
|
||||
/// explanation and derivation to explain
|
||||
/// why it is what it is, and how it's used.
|
||||
///
|
||||
/// It is left here for historical reasons, and
|
||||
/// for reference.
|
||||
///
|
||||
/// Reasons it's great:
|
||||
/// - First reason
|
||||
/// - Second reason
|
||||
///
|
||||
//pub const B: i32 = 1337;
|
||||
|
||||
/// This is yet another constant.
|
||||
///
|
||||
/// This has a similar fate as `B`.
|
||||
///
|
||||
/// Reasons it's useful:
|
||||
/// 1. First reason
|
||||
/// 2. Second reason
|
||||
///
|
||||
//pub const C: i32 = 8008;
|
||||
|
||||
/// This is still in use.
|
||||
pub const D: i32 = 20;
|
||||
|
||||
/// > blockquote code path
|
||||
///
|
||||
|
||||
/// bottom text
|
||||
pub const E: i32 = 20;
|
||||
|
||||
/// > blockquote code path
|
||||
///
|
||||
#[repr(C)]
|
||||
/// bottom text
|
||||
pub struct Foo(i32);
|
|
@ -1,43 +0,0 @@
|
|||
// https://github.com/rust-lang/rust-clippy/issues/12917
|
||||
#![warn(clippy::doc_lazy_continuation)]
|
||||
|
||||
/// This is a constant.
|
||||
///
|
||||
/// The meaning of which should not be explained.
|
||||
pub const A: i32 = 42;
|
||||
|
||||
/// This is another constant, no longer used.
|
||||
///
|
||||
/// This block of documentation has a long
|
||||
/// explanation and derivation to explain
|
||||
/// why it is what it is, and how it's used.
|
||||
///
|
||||
/// It is left here for historical reasons, and
|
||||
/// for reference.
|
||||
///
|
||||
/// Reasons it's great:
|
||||
/// - First reason
|
||||
/// - Second reason
|
||||
//pub const B: i32 = 1337;
|
||||
|
||||
/// This is yet another constant.
|
||||
///
|
||||
/// This has a similar fate as `B`.
|
||||
///
|
||||
/// Reasons it's useful:
|
||||
/// 1. First reason
|
||||
/// 2. Second reason
|
||||
//pub const C: i32 = 8008;
|
||||
|
||||
/// This is still in use.
|
||||
pub const D: i32 = 20;
|
||||
|
||||
/// > blockquote code path
|
||||
|
||||
/// bottom text
|
||||
pub const E: i32 = 20;
|
||||
|
||||
/// > blockquote code path
|
||||
#[repr(C)]
|
||||
/// bottom text
|
||||
pub struct Foo(i32);
|
|
@ -1,56 +0,0 @@
|
|||
error: doc list item without indentation
|
||||
--> tests/ui/doc/doc_lazy_blank_line.rs:23:5
|
||||
|
|
||||
LL | /// This is yet another constant.
|
||||
| ^
|
||||
|
|
||||
= help: if this is intended to be part of the list, indent 3 spaces
|
||||
= note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// - Second reason
|
||||
LL + ///
|
||||
|
|
||||
|
||||
error: doc list item without indentation
|
||||
--> tests/ui/doc/doc_lazy_blank_line.rs:32:5
|
||||
|
|
||||
LL | /// This is still in use.
|
||||
| ^
|
||||
|
|
||||
= help: if this is intended to be part of the list, indent 4 spaces
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// 2. Second reason
|
||||
LL + ///
|
||||
|
|
||||
|
||||
error: doc quote line without `>` marker
|
||||
--> tests/ui/doc/doc_lazy_blank_line.rs:37:5
|
||||
|
|
||||
LL | /// bottom text
|
||||
| ^
|
||||
|
|
||||
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// > blockquote code path
|
||||
LL + ///
|
||||
|
|
||||
|
||||
error: doc quote line without `>` marker
|
||||
--> tests/ui/doc/doc_lazy_blank_line.rs:42:5
|
||||
|
|
||||
LL | /// bottom text
|
||||
| ^
|
||||
|
|
||||
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// > blockquote code path
|
||||
LL + ///
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -7,9 +7,8 @@ fn one() {}
|
|||
|
||||
/// 1. first line
|
||||
/// lazy list continuations don't make warnings with this lint
|
||||
///
|
||||
//~^ ERROR: doc list item without indentation
|
||||
/// because they don't have the
|
||||
/// because they don't have the
|
||||
//~^ ERROR: doc list item without indentation
|
||||
fn two() {}
|
||||
|
||||
|
@ -20,9 +19,8 @@ fn three() {}
|
|||
|
||||
/// - first line
|
||||
/// lazy list continuations don't make warnings with this lint
|
||||
///
|
||||
//~^ ERROR: doc list item without indentation
|
||||
/// because they don't have the
|
||||
/// because they don't have the
|
||||
//~^ ERROR: doc list item without indentation
|
||||
fn four() {}
|
||||
|
||||
|
@ -33,9 +31,8 @@ fn five() {}
|
|||
|
||||
/// - - first line
|
||||
/// this will warn on the lazy continuation
|
||||
///
|
||||
//~^ ERROR: doc list item without indentation
|
||||
/// and so should this
|
||||
/// and so should this
|
||||
//~^ ERROR: doc list item without indentation
|
||||
fn six() {}
|
||||
|
||||
|
|
|
@ -30,12 +30,11 @@ error: doc list item without indentation
|
|||
LL | /// because they don't have the
|
||||
| ^
|
||||
|
|
||||
= help: if this is intended to be part of the list, indent 3 spaces
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// lazy list continuations don't make warnings with this lint
|
||||
LL + ///
|
||||
= help: if this is supposed to be its own paragraph, add a blank line
|
||||
help: indent this line
|
||||
|
|
||||
LL | /// because they don't have the
|
||||
| +++
|
||||
|
||||
error: doc list item without indentation
|
||||
--> tests/ui/doc/doc_lazy_list.rs:16:5
|
||||
|
@ -67,12 +66,11 @@ error: doc list item without indentation
|
|||
LL | /// because they don't have the
|
||||
| ^
|
||||
|
|
||||
= help: if this is intended to be part of the list, indent 4 spaces
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// lazy list continuations don't make warnings with this lint
|
||||
LL + ///
|
||||
= help: if this is supposed to be its own paragraph, add a blank line
|
||||
help: indent this line
|
||||
|
|
||||
LL | /// because they don't have the
|
||||
| ++++
|
||||
|
||||
error: doc list item without indentation
|
||||
--> tests/ui/doc/doc_lazy_list.rs:28:5
|
||||
|
@ -104,12 +102,11 @@ error: doc list item without indentation
|
|||
LL | /// and so should this
|
||||
| ^^^^
|
||||
|
|
||||
= help: if this is intended to be part of the list, indent 2 spaces
|
||||
help: if this should be its own paragraph, add a blank doc comment line
|
||||
|
|
||||
LL ~ /// this will warn on the lazy continuation
|
||||
LL + ///
|
||||
= help: if this is supposed to be its own paragraph, add a blank line
|
||||
help: indent this line
|
||||
|
|
||||
LL | /// and so should this
|
||||
| ++
|
||||
|
||||
error: doc list item without indentation
|
||||
--> tests/ui/doc/doc_lazy_list.rs:56:5
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![warn(clippy::duplicate_underscore_argument)]
|
||||
#[allow(dead_code, unused)]
|
||||
|
||||
fn join_the_dark_side(darth: i32, _darth: i32) {}
|
||||
//~^ ERROR: `darth` already exists, having another argument having almost the same name ma
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
|
||||
--> tests/ui/duplicate_underscore_argument.rs:4:23
|
||||
--> tests/ui/duplicate_underscore_argument.rs:3:23
|
||||
|
|
||||
LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
|
||||
| ^^^^^
|
||||
|
|
135
tests/ui/empty_line_after/doc_comments.1.fixed
Normal file
135
tests/ui/empty_line_after/doc_comments.1.fixed
Normal file
|
@ -0,0 +1,135 @@
|
|||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~vvv empty_line_after_doc_comments
|
||||
/// Meant to be an
|
||||
/// inner doc comment
|
||||
/// for the crate
|
||||
fn first_in_crate() {}
|
||||
|
||||
mod m {
|
||||
//~vvv empty_line_after_doc_comments
|
||||
/// Meant to be an
|
||||
/// inner doc comment
|
||||
/// for the module
|
||||
fn first_in_module() {}
|
||||
}
|
||||
|
||||
mod some_mod {
|
||||
//! This doc comment should *NOT* produce a warning
|
||||
|
||||
mod some_inner_mod {
|
||||
fn some_noop() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// # Indented
|
||||
/// Blank line
|
||||
fn indented() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This should produce a warning
|
||||
fn with_doc_and_newline() {}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This doc comment should produce a warning
|
||||
/** This is also a doc comment and is part of the warning
|
||||
*/
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
fn three_attributes() {}
|
||||
|
||||
mod misattributed {
|
||||
//~v empty_line_after_doc_comments
|
||||
/// docs for `old_code`
|
||||
// fn old_code() {}
|
||||
fn new_code() {}
|
||||
|
||||
//~vv empty_line_after_doc_comments
|
||||
/// Docs
|
||||
/// for OldA
|
||||
// struct OldA;
|
||||
/// Docs
|
||||
/// for OldB
|
||||
// struct OldB;
|
||||
/// Docs
|
||||
/// for Multiple
|
||||
#[allow(dead_code)]
|
||||
struct Multiple;
|
||||
}
|
||||
|
||||
mod block_comments {
|
||||
//~v empty_line_after_doc_comments
|
||||
/**
|
||||
* Meant to be inner doc comment
|
||||
*/
|
||||
fn first_in_module() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/**
|
||||
* Docs for `old_code`
|
||||
*/
|
||||
/* fn old_code() {} */
|
||||
/**
|
||||
* Docs for `new_code`
|
||||
*/
|
||||
fn new_code() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// Docs for `old_code2`
|
||||
/* fn old_code2() {} */
|
||||
/// Docs for `new_code2`
|
||||
fn new_code2() {}
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
/// Should not lint
|
||||
// some line comment
|
||||
/// gaps without an empty line
|
||||
struct LineComment;
|
||||
|
||||
/// This should *NOT* produce a warning because the empty line is inside a block comment
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyInBlockComment;
|
||||
|
||||
/// This should *NOT* produce a warning
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
/// Ignore the empty line inside a cfg_attr'd out attribute
|
||||
#[cfg_attr(any(), multiline(
|
||||
foo = 1
|
||||
|
||||
bar = 2
|
||||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
fn main() {}
|
144
tests/ui/empty_line_after/doc_comments.2.fixed
Normal file
144
tests/ui/empty_line_after/doc_comments.2.fixed
Normal file
|
@ -0,0 +1,144 @@
|
|||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~vvv empty_line_after_doc_comments
|
||||
//! Meant to be an
|
||||
//! inner doc comment
|
||||
//! for the crate
|
||||
|
||||
fn first_in_crate() {}
|
||||
|
||||
mod m {
|
||||
//~vvv empty_line_after_doc_comments
|
||||
//! Meant to be an
|
||||
//! inner doc comment
|
||||
//! for the module
|
||||
|
||||
fn first_in_module() {}
|
||||
}
|
||||
|
||||
mod some_mod {
|
||||
//! This doc comment should *NOT* produce a warning
|
||||
|
||||
mod some_inner_mod {
|
||||
fn some_noop() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// # Indented
|
||||
///
|
||||
/// Blank line
|
||||
fn indented() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This should produce a warning
|
||||
fn with_doc_and_newline() {}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This doc comment should produce a warning
|
||||
/** This is also a doc comment and is part of the warning
|
||||
*/
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
fn three_attributes() {}
|
||||
|
||||
mod misattributed {
|
||||
//~v empty_line_after_doc_comments
|
||||
// /// docs for `old_code`
|
||||
// fn old_code() {}
|
||||
|
||||
fn new_code() {}
|
||||
|
||||
//~vv empty_line_after_doc_comments
|
||||
// /// Docs
|
||||
// /// for OldA
|
||||
// struct OldA;
|
||||
|
||||
// /// Docs
|
||||
// /// for OldB
|
||||
// struct OldB;
|
||||
|
||||
/// Docs
|
||||
/// for Multiple
|
||||
#[allow(dead_code)]
|
||||
struct Multiple;
|
||||
}
|
||||
|
||||
mod block_comments {
|
||||
//~v empty_line_after_doc_comments
|
||||
/*!
|
||||
* Meant to be inner doc comment
|
||||
*/
|
||||
|
||||
fn first_in_module() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/*
|
||||
* Docs for `old_code`
|
||||
*/
|
||||
/* fn old_code() {} */
|
||||
|
||||
/**
|
||||
* Docs for `new_code`
|
||||
*/
|
||||
fn new_code() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
// /// Docs for `old_code2`
|
||||
/* fn old_code2() {} */
|
||||
|
||||
/// Docs for `new_code2`
|
||||
fn new_code2() {}
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
/// Should not lint
|
||||
// some line comment
|
||||
/// gaps without an empty line
|
||||
struct LineComment;
|
||||
|
||||
/// This should *NOT* produce a warning because the empty line is inside a block comment
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyInBlockComment;
|
||||
|
||||
/// This should *NOT* produce a warning
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
/// Ignore the empty line inside a cfg_attr'd out attribute
|
||||
#[cfg_attr(any(), multiline(
|
||||
foo = 1
|
||||
|
||||
bar = 2
|
||||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
fn main() {}
|
147
tests/ui/empty_line_after/doc_comments.rs
Normal file
147
tests/ui/empty_line_after/doc_comments.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~vvv empty_line_after_doc_comments
|
||||
/// Meant to be an
|
||||
/// inner doc comment
|
||||
/// for the crate
|
||||
|
||||
fn first_in_crate() {}
|
||||
|
||||
mod m {
|
||||
//~vvv empty_line_after_doc_comments
|
||||
/// Meant to be an
|
||||
/// inner doc comment
|
||||
/// for the module
|
||||
|
||||
fn first_in_module() {}
|
||||
}
|
||||
|
||||
mod some_mod {
|
||||
//! This doc comment should *NOT* produce a warning
|
||||
|
||||
mod some_inner_mod {
|
||||
fn some_noop() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// # Indented
|
||||
|
||||
/// Blank line
|
||||
fn indented() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This should produce a warning
|
||||
|
||||
fn with_doc_and_newline() {}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// This doc comment should produce a warning
|
||||
|
||||
/** This is also a doc comment and is part of the warning
|
||||
*/
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
fn three_attributes() {}
|
||||
|
||||
mod misattributed {
|
||||
//~v empty_line_after_doc_comments
|
||||
/// docs for `old_code`
|
||||
// fn old_code() {}
|
||||
|
||||
fn new_code() {}
|
||||
|
||||
//~vv empty_line_after_doc_comments
|
||||
/// Docs
|
||||
/// for OldA
|
||||
// struct OldA;
|
||||
|
||||
/// Docs
|
||||
/// for OldB
|
||||
// struct OldB;
|
||||
|
||||
/// Docs
|
||||
/// for Multiple
|
||||
#[allow(dead_code)]
|
||||
struct Multiple;
|
||||
}
|
||||
|
||||
mod block_comments {
|
||||
//~v empty_line_after_doc_comments
|
||||
/**
|
||||
* Meant to be inner doc comment
|
||||
*/
|
||||
|
||||
fn first_in_module() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/**
|
||||
* Docs for `old_code`
|
||||
*/
|
||||
/* fn old_code() {} */
|
||||
|
||||
/**
|
||||
* Docs for `new_code`
|
||||
*/
|
||||
fn new_code() {}
|
||||
|
||||
//~v empty_line_after_doc_comments
|
||||
/// Docs for `old_code2`
|
||||
/* fn old_code2() {} */
|
||||
|
||||
/// Docs for `new_code2`
|
||||
fn new_code2() {}
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
/// Should not lint
|
||||
// some line comment
|
||||
/// gaps without an empty line
|
||||
struct LineComment;
|
||||
|
||||
/// This should *NOT* produce a warning because the empty line is inside a block comment
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyInBlockComment;
|
||||
|
||||
/// This should *NOT* produce a warning
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
/// Ignore the empty line inside a cfg_attr'd out attribute
|
||||
#[cfg_attr(any(), multiline(
|
||||
foo = 1
|
||||
|
||||
bar = 2
|
||||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
fn main() {}
|
176
tests/ui/empty_line_after/doc_comments.stderr
Normal file
176
tests/ui/empty_line_after/doc_comments.stderr
Normal file
|
@ -0,0 +1,176 @@
|
|||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:6:1
|
||||
|
|
||||
LL | / /// for the crate
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn first_in_crate() {}
|
||||
| ------------------- the comment documents this function
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the comment should document the crate use an inner doc comment
|
||||
|
|
||||
LL ~ //! Meant to be an
|
||||
LL ~ //! inner doc comment
|
||||
LL ~ //! for the crate
|
||||
|
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:14:5
|
||||
|
|
||||
LL | / /// for the module
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn first_in_module() {}
|
||||
| -------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the comment should document the parent module use an inner doc comment
|
||||
|
|
||||
LL ~ //! Meant to be an
|
||||
LL ~ //! inner doc comment
|
||||
LL ~ //! for the module
|
||||
|
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:27:5
|
||||
|
|
||||
LL | / /// # Indented
|
||||
LL | |
|
||||
| |_
|
||||
LL | /// Blank line
|
||||
LL | fn indented() {}
|
||||
| ------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the documentation should include the empty line include it in the comment
|
||||
|
|
||||
LL | ///
|
||||
|
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:34:1
|
||||
|
|
||||
LL | / /// This should produce a warning
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn with_doc_and_newline() {}
|
||||
| ------------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty lines after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:44:1
|
||||
|
|
||||
LL | / /// This doc comment should produce a warning
|
||||
LL | |
|
||||
LL | | /** This is also a doc comment and is part of the warning
|
||||
LL | | */
|
||||
LL | |
|
||||
| |_
|
||||
...
|
||||
LL | fn three_attributes() {}
|
||||
| --------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:56:5
|
||||
|
|
||||
LL | / /// docs for `old_code`
|
||||
LL | | // fn old_code() {}
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn new_code() {}
|
||||
| ------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the doc comment should not document `new_code` comment it out
|
||||
|
|
||||
LL | // /// docs for `old_code`
|
||||
| ++
|
||||
|
||||
error: empty lines after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:63:5
|
||||
|
|
||||
LL | / /// for OldA
|
||||
LL | | // struct OldA;
|
||||
LL | |
|
||||
LL | | /// Docs
|
||||
LL | | /// for OldB
|
||||
LL | | // struct OldB;
|
||||
LL | |
|
||||
| |_
|
||||
...
|
||||
LL | struct Multiple;
|
||||
| --------------- the comment documents this struct
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
help: if the doc comment should not document `Multiple` comment it out
|
||||
|
|
||||
LL ~ // /// Docs
|
||||
LL ~ // /// for OldA
|
||||
LL | // struct OldA;
|
||||
LL |
|
||||
LL ~ // /// Docs
|
||||
LL ~ // /// for OldB
|
||||
|
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:78:5
|
||||
|
|
||||
LL | / /**
|
||||
LL | | * Meant to be inner doc comment
|
||||
LL | | */
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn first_in_module() {}
|
||||
| -------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the comment should document the parent module use an inner doc comment
|
||||
|
|
||||
LL | /*!
|
||||
| ~
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:85:5
|
||||
|
|
||||
LL | / /**
|
||||
LL | | * Docs for `old_code`
|
||||
LL | | */
|
||||
LL | | /* fn old_code() {} */
|
||||
LL | |
|
||||
| |_
|
||||
...
|
||||
LL | fn new_code() {}
|
||||
| ------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the doc comment should not document `new_code` comment it out
|
||||
|
|
||||
LL - /**
|
||||
LL + /*
|
||||
|
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:96:5
|
||||
|
|
||||
LL | / /// Docs for `old_code2`
|
||||
LL | | /* fn old_code2() {} */
|
||||
LL | |
|
||||
| |_
|
||||
LL | /// Docs for `new_code2`
|
||||
LL | fn new_code2() {}
|
||||
| -------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the doc comment should not document `new_code2` comment it out
|
||||
|
|
||||
LL | // /// Docs for `old_code2`
|
||||
| ++
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
103
tests/ui/empty_line_after/outer_attribute.1.fixed
Normal file
103
tests/ui/empty_line_after/outer_attribute.1.fixed
Normal file
|
@ -0,0 +1,103 @@
|
|||
//@aux-build:../auxiliary/proc_macro_attr.rs
|
||||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[crate_type = "lib"]
|
||||
fn first_in_crate() {}
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_attr;
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
/// some comment
|
||||
fn with_one_newline_and_comment() {}
|
||||
|
||||
#[inline]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
fn with_one_newline() {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod two_lines {
|
||||
//~v empty_line_after_outer_attr
|
||||
#[crate_type = "lib"]
|
||||
fn with_two_newlines() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[doc = "doc attributes should be considered attributes"]
|
||||
enum Baz {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[repr(C)]
|
||||
struct Foo {
|
||||
one: isize,
|
||||
two: isize,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[allow(dead_code)]
|
||||
mod foo {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
// Still lint cases where the empty line does not immediately follow the attribute
|
||||
fn comment_before_empty_line() {}
|
||||
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyLineInBlockComment;
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/5567
|
||||
#[rustfmt::skip]
|
||||
#[fake_async_trait]
|
||||
pub trait Bazz {
|
||||
fn foo() -> Vec<u8> {
|
||||
let _i = "";
|
||||
|
||||
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[dummy(string = "first line
|
||||
|
||||
second line
|
||||
")]
|
||||
pub struct Args;
|
||||
|
||||
fn main() {}
|
106
tests/ui/empty_line_after/outer_attribute.2.fixed
Normal file
106
tests/ui/empty_line_after/outer_attribute.2.fixed
Normal file
|
@ -0,0 +1,106 @@
|
|||
//@aux-build:../auxiliary/proc_macro_attr.rs
|
||||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#![crate_type = "lib"]
|
||||
|
||||
fn first_in_crate() {}
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_attr;
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
/// some comment
|
||||
fn with_one_newline_and_comment() {}
|
||||
|
||||
#[inline]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
fn with_one_newline() {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod two_lines {
|
||||
//~v empty_line_after_outer_attr
|
||||
#![crate_type = "lib"]
|
||||
|
||||
|
||||
fn with_two_newlines() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[doc = "doc attributes should be considered attributes"]
|
||||
enum Baz {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[repr(C)]
|
||||
struct Foo {
|
||||
one: isize,
|
||||
two: isize,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[allow(dead_code)]
|
||||
mod foo {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
// Still lint cases where the empty line does not immediately follow the attribute
|
||||
fn comment_before_empty_line() {}
|
||||
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyLineInBlockComment;
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/5567
|
||||
#[rustfmt::skip]
|
||||
#[fake_async_trait]
|
||||
pub trait Bazz {
|
||||
fn foo() -> Vec<u8> {
|
||||
let _i = "";
|
||||
|
||||
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[dummy(string = "first line
|
||||
|
||||
second line
|
||||
")]
|
||||
pub struct Args;
|
||||
|
||||
fn main() {}
|
112
tests/ui/empty_line_after/outer_attribute.rs
Normal file
112
tests/ui/empty_line_after/outer_attribute.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
//@aux-build:../auxiliary/proc_macro_attr.rs
|
||||
#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[crate_type = "lib"]
|
||||
|
||||
fn first_in_crate() {}
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_attr;
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
|
||||
/// some comment
|
||||
fn with_one_newline_and_comment() {}
|
||||
|
||||
#[inline]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
|
||||
fn with_one_newline() {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod two_lines {
|
||||
//~v empty_line_after_outer_attr
|
||||
#[crate_type = "lib"]
|
||||
|
||||
|
||||
fn with_two_newlines() {}
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[doc = "doc attributes should be considered attributes"]
|
||||
|
||||
enum Baz {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[repr(C)]
|
||||
|
||||
struct Foo {
|
||||
one: isize,
|
||||
two: isize,
|
||||
}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[allow(dead_code)]
|
||||
|
||||
mod foo {}
|
||||
|
||||
//~v empty_line_after_outer_attr
|
||||
#[inline]
|
||||
// Still lint cases where the empty line does not immediately follow the attribute
|
||||
|
||||
fn comment_before_empty_line() {}
|
||||
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4,
|
||||
}
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct EmptyLineInBlockComment;
|
||||
|
||||
#[crate_type = "lib"]
|
||||
/* test */
|
||||
pub struct BlockComment;
|
||||
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/5567
|
||||
#[rustfmt::skip]
|
||||
#[fake_async_trait]
|
||||
pub trait Bazz {
|
||||
fn foo() -> Vec<u8> {
|
||||
let _i = "";
|
||||
|
||||
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[dummy(string = "first line
|
||||
|
||||
second line
|
||||
")]
|
||||
pub struct Args;
|
||||
|
||||
fn main() {}
|
103
tests/ui/empty_line_after/outer_attribute.stderr
Normal file
103
tests/ui/empty_line_after/outer_attribute.stderr
Normal file
|
@ -0,0 +1,103 @@
|
|||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:5:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn first_in_crate() {}
|
||||
| ------------------- the attribute applies to this function
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
|
||||
= help: if the empty line is unintentional remove it
|
||||
help: if the attribute should apply to the crate use an inner attribute
|
||||
|
|
||||
LL | #![crate_type = "lib"]
|
||||
| +
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:13:1
|
||||
|
|
||||
LL | / #[inline]
|
||||
LL | |
|
||||
| |_
|
||||
LL | /// some comment
|
||||
LL | fn with_one_newline_and_comment() {}
|
||||
| --------------------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:23:1
|
||||
|
|
||||
LL | / #[inline]
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn with_one_newline() {}
|
||||
| --------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty lines after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:30:5
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn with_two_newlines() {}
|
||||
| ---------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
help: if the attribute should apply to the parent module use an inner attribute
|
||||
|
|
||||
LL | #![crate_type = "lib"]
|
||||
| +
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:37:1
|
||||
|
|
||||
LL | / #[doc = "doc attributes should be considered attributes"]
|
||||
LL | |
|
||||
| |_
|
||||
LL | enum Baz {
|
||||
| -------- the attribute applies to this enum
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:45:1
|
||||
|
|
||||
LL | / #[repr(C)]
|
||||
LL | |
|
||||
| |_
|
||||
LL | struct Foo {
|
||||
| ---------- the attribute applies to this struct
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:53:1
|
||||
|
|
||||
LL | / #[allow(dead_code)]
|
||||
LL | |
|
||||
| |_
|
||||
LL | mod foo {}
|
||||
| ------- the attribute applies to this module
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:58:1
|
||||
|
|
||||
LL | / #[inline]
|
||||
LL | | // Still lint cases where the empty line does not immediately follow the attribute
|
||||
LL | |
|
||||
| |_
|
||||
LL | fn comment_before_empty_line() {}
|
||||
| ------------------------------ the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
//@aux-build:proc_macro_attr.rs
|
||||
#![warn(clippy::empty_line_after_doc_comments)]
|
||||
#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![rustfmt::skip]
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_attr;
|
||||
|
||||
mod some_mod {
|
||||
//! This doc comment should *NOT* produce a warning
|
||||
|
||||
mod some_inner_mod {
|
||||
fn some_noop() {}
|
||||
}
|
||||
}
|
||||
|
||||
/// This should produce a warning
|
||||
|
||||
fn with_doc_and_newline() { assert!(true)}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
/// some comment
|
||||
fn with_one_newline_and_comment() { assert!(true) }
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() { assert!(true) }
|
||||
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
fn with_one_newline() { assert!(true) }
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
|
||||
fn with_two_newlines() { assert!(true) }
|
||||
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
enum Baz {
|
||||
One,
|
||||
Two
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
struct Foo {
|
||||
one: isize,
|
||||
two: isize
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
mod foo {
|
||||
}
|
||||
|
||||
/// This doc comment should produce a warning
|
||||
|
||||
/** This is also a doc comment and should produce a warning
|
||||
*/
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(missing_docs)]
|
||||
#[allow(missing_docs)]
|
||||
fn three_attributes() { assert!(true) }
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4
|
||||
}
|
||||
|
||||
// This should *NOT* produce a warning because the empty line is inside a block comment
|
||||
#[crate_type = "lib"]
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct S;
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/* test */
|
||||
pub struct T;
|
||||
|
||||
// This should *NOT* produce a warning
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/5567
|
||||
#[fake_async_trait]
|
||||
pub trait Bazz {
|
||||
fn foo() -> Vec<u8> {
|
||||
let _i = "";
|
||||
|
||||
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[dummy(string = "first line
|
||||
|
||||
second line
|
||||
")]
|
||||
pub struct Args;
|
||||
|
||||
fn main() {}
|
|
@ -1,37 +0,0 @@
|
|||
error: 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 `//`?
|
||||
--> tests/ui/empty_line_after_doc_comments.rs:18:1
|
||||
|
|
||||
LL | / /// This should produce a warning
|
||||
LL | |
|
||||
LL | | fn with_doc_and_newline() { assert!(true)}
|
||||
| |_
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
|
||||
|
||||
error: 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 `//`?
|
||||
--> tests/ui/empty_line_after_doc_comments.rs:68:1
|
||||
|
|
||||
LL | / /// This doc comment should produce a warning
|
||||
LL | |
|
||||
LL | | /** This is also a doc comment and should produce a warning
|
||||
LL | | */
|
||||
... |
|
||||
LL | | #[allow(missing_docs)]
|
||||
LL | | fn three_attributes() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: 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 `//`?
|
||||
--> tests/ui/empty_line_after_doc_comments.rs:70:1
|
||||
|
|
||||
LL | / /** This is also a doc comment and should produce a warning
|
||||
LL | | */
|
||||
LL | |
|
||||
LL | | // This should *NOT* produce a warning
|
||||
... |
|
||||
LL | | #[allow(missing_docs)]
|
||||
LL | | fn three_attributes() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
//@aux-build:proc_macro_attr.rs
|
||||
#![warn(clippy::empty_line_after_outer_attr)]
|
||||
#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![rustfmt::skip]
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_attr;
|
||||
|
||||
// This should produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
/// some comment
|
||||
fn with_one_newline_and_comment() { assert!(true) }
|
||||
|
||||
// This should not produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/// some comment
|
||||
fn with_no_newline_and_comment() { assert!(true) }
|
||||
|
||||
|
||||
// This should produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
fn with_one_newline() { assert!(true) }
|
||||
|
||||
// This should produce a warning, too
|
||||
#[crate_type = "lib"]
|
||||
|
||||
|
||||
fn with_two_newlines() { assert!(true) }
|
||||
|
||||
|
||||
// This should produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
enum Baz {
|
||||
One,
|
||||
Two
|
||||
}
|
||||
|
||||
// This should produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
struct Foo {
|
||||
one: isize,
|
||||
two: isize
|
||||
}
|
||||
|
||||
// This should produce a warning
|
||||
#[crate_type = "lib"]
|
||||
|
||||
mod foo {
|
||||
}
|
||||
|
||||
/// This doc comment should not produce a warning
|
||||
|
||||
/** This is also a doc comment and should not produce a warning
|
||||
*/
|
||||
|
||||
// This should not produce a warning
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(missing_docs)]
|
||||
#[allow(missing_docs)]
|
||||
fn three_attributes() { assert!(true) }
|
||||
|
||||
// This should not produce a warning
|
||||
#[doc = "
|
||||
Returns the escaped value of the textual representation of
|
||||
|
||||
"]
|
||||
pub fn function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// This should not produce a warning
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FooFighter {
|
||||
Bar1,
|
||||
|
||||
Bar2,
|
||||
|
||||
Bar3,
|
||||
|
||||
Bar4
|
||||
}
|
||||
|
||||
// This should not produce a warning because the empty line is inside a block comment
|
||||
#[crate_type = "lib"]
|
||||
/*
|
||||
|
||||
*/
|
||||
pub struct S;
|
||||
|
||||
// This should not produce a warning
|
||||
#[crate_type = "lib"]
|
||||
/* test */
|
||||
pub struct T;
|
||||
|
||||
// This should not produce a warning
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/5567
|
||||
#[fake_async_trait]
|
||||
pub trait Bazz {
|
||||
fn foo() -> Vec<u8> {
|
||||
let _i = "";
|
||||
|
||||
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[dummy(string = "first line
|
||||
|
||||
second line
|
||||
")]
|
||||
pub struct Args;
|
||||
|
||||
fn main() {}
|
|
@ -1,54 +0,0 @@
|
|||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:11:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | | /// some comment
|
||||
LL | | fn with_one_newline_and_comment() { assert!(true) }
|
||||
| |_
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
|
||||
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:23:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | | fn with_one_newline() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:28:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
... |
|
||||
LL | | fn with_two_newlines() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:35:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | | enum Baz {
|
||||
| |_
|
||||
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:43:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | | struct Foo {
|
||||
| |_
|
||||
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> tests/ui/empty_line_after_outer_attribute.rs:51:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
LL | |
|
||||
LL | | mod foo {
|
||||
| |_
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#[warn(clippy::exit)]
|
||||
#![warn(clippy::exit)]
|
||||
|
||||
fn not_main() {
|
||||
if true {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[warn(clippy::exit)]
|
||||
#![warn(clippy::exit)]
|
||||
|
||||
fn also_not_main() {
|
||||
std::process::exit(3);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[warn(clippy::exit)]
|
||||
#![warn(clippy::exit)]
|
||||
|
||||
fn main() {
|
||||
if true {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue