rfcs/text/2476-clippy-uno.md

63 KiB
Raw Permalink Blame History

Summary

Release Clippy 1.0, in preparation for it being shipped via rustup and eventually available via Rust Stable.

Motivation

See also: The Future of Clippy

Clippy, the linter for Rust, has been a nightly-only plugin to Rust for many years. In that time, it's grown big, but it's nightly-only nature makes it pretty hard to use.

The eventual plan is to integrate it in Rustup à la Rustfmt/RLS so that you can simply fetch prebuilt binaries for your system and cargo clippy Just Works ™️. In preparation for this, we'd like to nail down various things about its lints and their categorization.

Guide-level explanation

Usage and lint philosophy

We expect Clippy to be used via cargo clippy.

Clippy aims to follow the general Rust style. It may be somewhat opiniated in some situations.

In general Clippy is intended to be used with a liberal sprinkling of #[allow()] and #[warn()]; it is okay to disagree with Clippy's choices. This is a weaker philosophy than that behind rustc's lints, where usually flipping one is an indication of a very specialized situation.

Lint attributes

Currently to allow/deny Clippy lints you have to #[cfg_attr(clippy, allow(lintname))] which is somewhat tedious.

The compiler should support something like #[allow(clippy::lintname)] which won't attempt to warn about nonexistent lints at all when not running Clippy.

Stability guarantees

Clippy will have the same idea of lint stability as rustc; essentially we do not guarantee stability under #[deny(lintname)]. This is not a problem since deny only affects the current crate (dependencies have their lints capped) so at most youll be forced to slap on an #[allow()] for your own crate following a Rust upgrade.

This means that we will never remove lints. We may recategorize lints, and we may "deprecate" them. Deprecation "removes" them by removing their functionality and marking them as deprecated, which may cause further warnings but cannot cause a compiler error.

It also means that we won't make fundamentally large changes to lints. You can expect that turning on a lint will keep it behaving mostly similarly over time, unless it is removed. The kinds of changes we will make are:

  • Adding entirely new lints
  • Fixing false positives (A lint may no longer lint in a buggy case)
  • Fixing false negatives (A case where the lint should be linting but doesnt is fixed)
  • Bugfixes (When the lint panics or does something otherwise totally broken)

When fixing false negatives this will usually be fixing things that can be understood as comfortably within the scope of the lint as documented/named. For example, a lint on having the type Box<Vec<_>> may be changed to also catch Box<Vec<T>> where T is generic, but will not be changed to also catch Box<String> (which can be linted on for the same reasons).

An exception to this is the "nursery" lints — Clippy has a lint category for unpolished lints called the "nursery" which are allow-by-default. These may experience radical changes, however they will never be entirely "removed" either.

Pre-1.0 we may also flush out all of the deprecated lints.

The configuration file for clippy, clippy.toml, is not stabilized in this RFC. Instead, we propose to require clippy.toml users set a clippy_toml_is_unstable_and_may_go_away option.

The interface and existence of cargo-clippy is also not stabilized in this RFC. We will continue shipping it with rustup, but it may be replaced in the future with a combined cargo lint command.

Lint audit and categories

A couple months ago we did a lint audit to recategorize all the Clippy lints. The Reference-Level explanation below contains a list of all of these lints as currently categorized.

The categories we came up with are:

  • Correctness (Deny): Probable bugs, e.g. calling .clone() on &&T, which clones the (Copy) reference and not the actual type
  • Style (Warn): Style issues; where the fix usually doesn't semantically change the code but instead changes naming/formatting. For example, having a method named into_foo() that doesn't take self by-move
  • Complexity (Warn): For detecting unnecessary code complexities and helping simplify them. For example, a lint that asks you to replace .filter(..).next() with .find(..)
  • Perf (Warn): Detecting potential performance footguns, like using Box<Vec<T>> or calling .or(foo()) instead of or_else(foo).
  • Pedantic (Allow): Controversial or exceedingly pedantic lints
  • Nursery (Allow): For lints which are buggy or need more work
  • Cargo (Allow): Lints about your Cargo setup
  • Restriction (Allow): Lints for things which are not usually a problem, but may be something specific situations may dictate disallowing.
  • Internal (Allow): Nothing to see here, move along
  • Deprecated (Allow): Empty lints that exist to ensure that #[allow(lintname)] still compiles after the lint was deprecated.

Lints can only belong to one lint group at a time, and the lint group defines the lint level. There is a bunch of overlap between the style and complexity groups -- a lot of style issues are also complexity issues and vice versa. We separate these groups so that people can opt in to the complexity lints without having to opt in to Clippy's style.

Compiler uplift

The compiler has historically had a "no new lints" policy, partly with the desire that lints would incubate outside of the compiler (so usually in Clippy). This feels like a good time to look into uplifting these lints.

This RFC does not yet propose lints to be uplifted, but the intention is that the RFC discussion will bring up lints that the community feels should be uplifted and we can list them here.

Such an uplift may change the lint level; correctness lints are Deny by default in Clippy but would probably switch to Warn if uplifted since the compiler is more conservative here (Using Clippy is in itself an opt-in to a "please annoy me more" mode).

We'd also like to establish a rough policy for future lints here: Some correctness lints should probably belong in the compiler, whereas style/complexity/etc lints should probably belong in Clippy. Lints may be incubated in Clippy, of course.

I don't think the compler will want all correctness lints here, however if the lint is about a common enough situation where it being not a bug is an exceedingly rare case (i.e. very low false positive frequency) it should probably belong in the compiler.

What lints belong in clippy?

Essentially, we consider the categorization itself to be a definition of boundaries -- if it doesn't fit in the categories, it doesn't fit in clippy (or needs an RFC for, specifically).

In itself this isn't complete, we explicitly have a "pedantic" group that's kinda ill defined.

The rules for the main categories (style/complexity/correctness/perf -- things which are warn or deny by default) are:

  • Main category lints need to be something the community has general agreement on. This does not mean each lint addition must go through an RFC-like process. Instead, this is to be judged by the maintainers during the review of the lint pull request (taking into account objections raised if any). If the lint turns out to be controversial in the future we can flip it off or recategorize it.
  • Generally, if a lint is triggered, this should be useful to most Rust programmers seeing it most of the time.
  • It is okay for a lint to deal with niche code that usually won't even be triggered. Lints can target subsets of the community provided they don't randomly trigger for others.
  • It is okay if the lint has some false positives (cases where it lints for things which are actually okay), as long as they don't dominate.
  • It is also okay if the lint warns about things which people do not feel are worth fixing -- i.e. the programmer agrees that it is a problem but does not wish to fix this. Using clippy is itself an opt-in to more finicky linting. However, this is sometimes an indicator of such a lint potentially belonging in the pedantic group.
  • Clippy is meant to be used with a liberal sprinkling of allow. If there's a specific use case where a lint doesn't apply, and the solution is to slap allow on it, that's okay. A minor level of false positives like this is to be tolerated. Similarly, style lints are allowed to be about things a lot of people don't care about (i.e. they don't prefer the opposite style, they just don't care).
  • Clippy lints do deal with the visual presentation of your code, but only for things which rustfmt doesn't or can't handle. So, for example, rustfmt will not ask you to replace if {} else { if {} } with if {} else if {}, but clippy might. There is some overlap in this area and we expect to work with rustfmt on precisely figuring out what goes where. Such lints are usually style lints or complexity lints.
  • Clippy lints are allowed to make some kind of semantic changes, but not all:
    • The general rule is that clippy will not attempt to change what it perceives to be the intent of the code, but will rather change the code to make it closer to the intent or make it achieve that intent better
    • Clippy lints do deal with potential typos and mistakes. For example, clippy will detect for x in y.next() which is very likely a bug (you either mean if let or mean to unwrap). Such lints are usually correctness lints.
    • Clippy lints also do deal with misunderstandings of Rust, for example code doing foo == NaN is a misunderstanding of how Rust floats work. These are also usually correctness lints.
    • Clippy lints do not comment on the business logic of your program. This comes from the "perceived intent" rule above, changes to business logic are a change to perceived intent.
    • Clippy lints do ask you to make semantic changes that achieve the same effect with perhaps better performance. Such lints are usually perf lints.

For the other categories (these are allow by default):

  • Lints which are "pedantic" should still roughly fall into one of the main categories, just that they are too annoying (or possibly controversial) to be warn by default. So a lint must follow all the above rules if pedantic, but is allowed to be "too finicky to fix", and may have looser consensus (i.e. some controversy).
  • Similar rules for "nursery" except their reason for being allow by default is lack of maturity (i.e. the lint is buggy or still needs some thought)
  • "restriction" lints follow all the rules for semantic changes, but do not bother with the rules for the lint being useful to most rust programmers. A restriction lint must still be such that you have a good reason to enable it — "I dislike such code" is insufficient — but will likely be a lint most programmers wish to keep off by default for most of their code. The goal of restriction lints is to provide tools with which you can supplement the language checks in very specific cases where you need it, e.g. forbidding panics from a certain area of code.
  • "cargo" lints follow the same rules as pedantic lints (we only have one of them right now, so we may be experimenting with this in the future)

Reference-level explanation

Lint categorization

This categorization can be browsed online.

Please leave comments on thoughts about these lints -- if their categorization is correct, if they should exist at all, and if we should be uplifting them to the compiler.

For ease of review, the lints below are as they were listed in the original RFC. The proposed changes are:

  • shadow_unrelated be moved from restriction to pedantic
  • Various lints be uplifted to the compiler (and potentially renamed). This is tracked in https://github.com/rust-lang/rust/issues/53224
  • explicit_iter_loop and explicit_into_iter_loop be moved from style to pedantic

correctness (Deny)

  • for_loop_over_option: Checks for for loops over Option values.
  • eq_op: Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (==, >, etc., &&, ||, &, |, ^, - and /).
  • iter_next_loop: Checks for loops on x.next().
  • deprecated_semver: Checks for #[deprecated] annotations with a since field that is not a valid semantic version.
  • drop_copy: Checks for calls to std::mem::drop with a value that derives the Copy trait
  • not_unsafe_ptr_arg_deref: Checks for public functions that dereferences raw pointer arguments but are not marked unsafe.
  • logic_bug: Checks for boolean expressions that contain terminals that can be eliminated.
  • clone_double_ref: Checks for usage of .clone() on an &&T.
  • almost_swapped: Checks for foo = bar; bar = foo sequences.
  • possible_missing_comma: Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.
  • wrong_transmute: Checks for transmutes that can't ever be correct on any architecture.
  • invalid_regex: Checks regex creation (with Regex::new,RegexBuilder::new or RegexSet::new) for correct regex syntax.
  • bad_bit_mask: Checks for incompatible bit masks in comparisons.
  • drop_ref: Checks for calls to std::mem::drop with a reference instead of an owned value.
  • derive_hash_xor_eq: Checks for deriving Hash but implementing PartialEq explicitly or vice versa.
  • useless_attribute: Checks for extern crate and use items annotated with lint attributes
  • temporary_cstring_as_ptr: Checks for getting the inner pointer of a temporary CString.
  • min_max: Checks for expressions where std::cmp::min and max are used to clamp values, but switched so that the result is constant.
  • unit_cmp: Checks for comparisons to unit.
  • reverse_range_loop: Checks for loops over ranges x..y where both x and y are constant and x is greater or equal to y, unless the range is reversed or has a negative .step_by(_).
  • erasing_op: Checks for erasing operations, e.g. x * 0.
  • suspicious_op_assign_impl: Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.
  • float_cmp: Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called *eq* (which probably implement equality for a type involving floats).
  • zero_width_space: Checks for the Unicode zero-width space in the code.
  • fn_to_numeric_cast_with_truncation: Checks for casts of a function pointer to a numeric type not enough to store address.
  • suspicious_arithmetic_impl: Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.
  • approx_constant: Checks for floating point literals that approximate constants which are defined in std::f32::consts or std::f64::consts, respectively, suggesting to use the predefined constant.
  • while_immutable_condition: Checks whether variables used within while loop condition can be (and are) mutated in the body.
  • never_loop: Checks for loops that will always break, return or continue an outer loop.
  • nonsensical_open_options: Checks for duplicate open options as well as combinations that make no sense.
  • forget_copy: Checks for calls to std::mem::forget with a value that derives the Copy trait
  • if_same_then_else: Checks for if/else with the same body as the then part and the else part.
  • cast_ptr_alignment: Checks for casts from a less-strictly-aligned pointer to a more-strictly-aligned pointer
  • ifs_same_cond: Checks for consecutive ifs with the same condition.
  • out_of_bounds_indexing: Checks for out of bounds array indexing with a constant index.
  • modulo_one: Checks for getting the remainder of a division by one.
  • inline_fn_without_body: Checks for #[inline] on trait methods without bodies
  • cmp_nan: Checks for comparisons to NaN.
  • ineffective_bit_mask: Checks for bit masks in comparisons which can be removed without changing the outcome.
  • infinite_iter: Checks for iteration that is guaranteed to be infinite.
  • mut_from_ref: This lint checks for functions that take immutable references and return mutable ones.
  • unused_io_amount: Checks for unused written/read amount.
  • invalid_ref: Checks for creation of references to zeroed or uninitialized memory.
  • serde_api_misuse: Checks for mis-uses of the serde API.
  • forget_ref: Checks for calls to std::mem::forget with a reference instead of an owned value.
  • absurd_extreme_comparisons: Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a case that is always true or always false. Only integer and boolean types are checked.
  • for_loop_over_result: Checks for for loops over Result values.
  • iterator_step_by_zero: Checks for calling .step_by(0) on iterators, which never terminates.
  • enum_clike_unportable_variant: Checks for C-like enumerations that are repr(isize/usize) and have values that don't fit into an i32.

style (Warn)

  • inconsistent_digit_grouping: Warns if an integral or floating-point constant is grouped inconsistently with underscores.
  • get_unwrap: Checks for use of .get().unwrap() (or .get_mut().unwrap) on a standard library type which implements Index
  • match_bool: Checks for matches where match expression is a bool. It suggests to replace the expression with an if...else block.
  • cmp_null: This lint checks for equality comparisons with ptr::null
  • write_with_newline: This lint warns when you use write!() with a format string that ends in a newline.
  • unneeded_field_pattern: Checks for structure field patterns bound to wildcards.
  • new_without_default_derive: Checks for types with a fn new() -> Self method and no implementation of Default, where the Default can be derived by #[derive(Default)].
  • zero_ptr: Catch casts from 0 to some pointer type
  • wrong_self_convention: Checks for methods with certain name prefixes and which doesn't match how self is taken.
  • iter_skip_next: Checks for use of .skip(x).next() on iterators.
  • large_digit_groups: Warns if the digits of an integral or floating-point constant are grouped into groups that are too large.
  • range_minus_one: Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g. x..=(y-1).
  • regex_macro: Checks for usage of regex!(_) which (as of now) is usually slower than Regex::new(_) unless called in a loop (which is a bad idea anyway).
  • op_ref: Checks for arguments to == which have their address taken to satisfy a bound and suggests to dereference the other argument instead
  • question_mark: Checks for expressions that could be replaced by the question mark operator
  • redundant_closure: Checks for closures which just call another function where the function can be called directly. unsafe functions or calls where types get adjusted are ignored.
  • print_with_newline: This lint warns when you use print!() with a format string that ends in a newline.
  • match_ref_pats: Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression instead. It also checks for if let &foo = bar blocks.
  • ptr_arg: This lint checks for function arguments of type &String or &Vec unless the references are mutable. It will also suggest you replace .clone() calls with the appropriate .to_owned()/to_string() calls.
  • chars_last_cmp: Checks for usage of .chars().last() or .chars().next_back() on a str to check if it ends with a given char.
  • assign_op_pattern: Checks for a = a op b or a = b commutative_op a patterns.
  • mixed_case_hex_literals: Warns on hexadecimal literals with mixed-case letter digits.
  • blacklisted_name: Checks for usage of blacklisted names for variables, such as foo.
  • double_neg: Detects expressions of the form --x.
  • unnecessary_fold: Checks for using fold when a more succinct alternative exists. Specifically, this checks for folds which could be replaced by any, all, sum or product.
  • let_unit_value: Checks for binding a unit value.
  • needless_range_loop: Checks for looping over the range of 0..len of some collection just to get the values by index.
  • excessive_precision: Checks for float literals with a precision greater than that supported by the underlying type
  • duplicate_underscore_argument: Checks for function arguments having the similar names differing by an underscore.
  • println_empty_string: This lint warns when you use println!("") to print a newline.
  • panic_params: Checks for missing parameters in panic!.
  • writeln_empty_string: This lint warns when you use writeln!(buf, "") to print a newline.
  • infallible_destructuring_match: Checks for matches being used to destructure a single-variant enum or tuple struct where a let will suffice.
  • block_in_if_condition_stmt: Checks for if conditions that use blocks containing statements, or conditions that use closures with blocks.
  • unreadable_literal: Warns if a long integral or floating-point constant does not contain underscores.
  • unsafe_removed_from_name: Checks for imports that remove "unsafe" from an item's name.
  • builtin_type_shadow: Warns if a generic shadows a built-in type.
  • option_map_or_none: Checks for usage of _.map_or(None, _).
  • neg_multiply: Checks for multiplication by -1 as a form of negation.
  • const_static_lifetime: Checks for constants with an explicit 'static lifetime.
  • explicit_iter_loop: Checks for loops on x.iter() where &x will do, and suggests the latter.
  • single_match: Checks for matches with a single arm where an if let will usually suffice.
  • for_kv_map: Checks for iterating a map (HashMap or BTreeMap) and ignoring either the keys or values.
  • if_let_some_result: * Checks for unnecessary ok() in if let.
  • collapsible_if: Checks for nested if statements which can be collapsed by &&-combining their conditions and for else { if ... } expressions that can be collapsed to else if ....
  • len_without_is_empty: Checks for items that implement .len() but not .is_empty().
  • unnecessary_mut_passed: Detects giving a mutable reference to a function that only requires an immutable reference.
  • useless_let_if_seq: Checks for variable declarations immediately followed by a conditional affectation.
  • new_ret_no_self: Checks for new not returning Self.
  • write_literal: This lint warns about the use of literals as write!/writeln! args.
  • block_in_if_condition_expr: Checks for if conditions that use blocks to contain an expression.
  • toplevel_ref_arg: Checks for function arguments and let bindings denoted as ref.
  • suspicious_else_formatting: Checks for formatting of else if. It lints if the else and if are not on the same line or the else seems to be missing.
  • fn_to_numeric_cast: Checks for casts of a function pointer to a numeric type except usize.
  • let_and_return: Checks for let-bindings, which are subsequently returned.
  • len_zero: Checks for getting the length of something via .len() just to compare to zero, and suggests using .is_empty() where applicable.
  • suspicious_assignment_formatting: Checks for use of the non-existent =*, =! and =- operators.
  • redundant_field_names: Checks for fields in struct literals where shorthands could be used.
  • string_lit_as_bytes: Checks for the as_bytes method called on string literals that contain only ASCII characters.
  • verbose_bit_mask: Checks for bit masks that can be replaced by a call to trailing_zeros
  • map_clone: Checks for mapping clone() over an iterator.
  • new_without_default: Checks for types with a fn new() -> Self method and no implementation of Default.
  • should_implement_trait: Checks for methods that should live in a trait implementation of a std trait (see llogiq's blog post for further information) instead of an inherent implementation.
  • match_wild_err_arm: Checks for arm which matches all errors with Err(_) and take drastic actions like panic!.
  • iter_cloned_collect: Checks for the use of .cloned().collect() on slice to create a Vec.
  • module_inception: Checks for modules that have the same name as their parent module
  • many_single_char_names: Checks for too many variables whose name consists of a single character.
  • enum_variant_names: Detects enumeration variants that are prefixed or suffixed by the same characters.
  • string_extend_chars: Checks for the use of .extend(s.chars()) where s is a &str or String.
  • needless_return: Checks for return statements at the end of a block.
  • print_literal: This lint warns about the use of literals as print!/println! args.
  • implicit_hasher: Checks for public impl or fn missing generalization over different hashers and implicitly defaulting to the default hashing algorithm (SipHash).
  • needless_pass_by_value: Checks for functions taking arguments by value, but not consuming them in its body.
  • trivial_regex: Checks for trivial regex creation (with Regex::new, RegexBuilder::new or RegexSet::new).
  • while_let_on_iterator: Checks for while let expressions on iterators.
  • redundant_pattern: Checks for patterns in the form name @ _.
  • match_overlapping_arm: Checks for overlapping match arms.
  • just_underscores_and_digits: Checks if you have variables whose name consists of just underscores and digits.
  • ok_expect: Checks for usage of ok().expect(..).
  • empty_loop: Checks for empty loop expressions.
  • explicit_into_iter_loop: Checks for loops on y.into_iter() where y will do, and suggests the latter.
  • if_let_redundant_pattern_matching: Lint for redundant pattern matching over Result or Option

complexity (Warn)

  • option_option: Checks for use of Option<Option<_>> in function signatures and type definitions
  • precedence: Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:
    • mixed usage of arithmetic and bit shifting/combining operators without parentheses
    • a "negative" numeric literal (which is really a unary - followed by a numeric literal) followed by a method call
  • useless_transmute: Checks for transmutes to the original type of the object and transmutes that could be a cast.
  • partialeq_ne_impl: Checks for manual re-implementations of PartialEq::ne.
  • redundant_closure_call: Detects closures called in the same expression where they are defined.
  • manual_swap: Checks for manual swapping.
  • option_map_unit_fn: Checks for usage of option.map(f) where f is a function or closure that returns the unit type.
  • overflow_check_conditional: Detects classic underflow/overflow checks.
  • transmute_ptr_to_ref: Checks for transmutes from a pointer to a reference.
  • chars_next_cmp: Checks for usage of .chars().next() on a str to check if it starts with a given char.
  • transmute_bytes_to_str: Checks for transmutes from a &[u8] to a &str.
  • identity_conversion: Checks for always-identical Into/From conversions.
  • double_parens: Checks for unnecessary double parentheses.
  • zero_divided_by_zero: Checks for 0.0 / 0.0.
  • useless_asref: Checks for usage of .as_ref() or .as_mut() where the types before and after the call are the same.
  • too_many_arguments: Checks for functions with too many parameters.
  • range_zip_with_len: Checks for zipping a collection with the range of 0.._.len().
  • temporary_assignment: Checks for construction of a structure or tuple just to assign a value in it.
  • no_effect: Checks for statements which have no effect.
  • short_circuit_statement: Checks for the use of short circuit boolean conditions as a statement.
  • cast_lossless: Checks for on casts between numerical types that may be replaced by safe conversion functions.
  • unnecessary_operation: Checks for expression statements that can be reduced to a sub-expression.
  • cyclomatic_complexity: Checks for methods with high cyclomatic complexity.
  • while_let_loop: Detects loop + match combinations that are easier written as a while let loop.
  • needless_update: Checks for needlessly including a base struct on update when all fields are changed anyway.
  • identity_op: Checks for identity operations, e.g. x + 0.
  • search_is_some: Checks for an iterator search (such as find(), position(), or rposition()) followed by a call to is_some().
  • useless_format: Checks for the use of format!("string literal with no argument") and format!("{}", foo) where foo is a string.
  • diverging_sub_expression: Checks for diverging calls that are not match arms or statements.
  • transmute_ptr_to_ptr: Checks for transmutes from a pointer to a pointer, or from a reference to a reference.
  • crosspointer_transmute: Checks for transmutes between a type T and *T.
  • needless_borrowed_reference: Checks for useless borrowed references.
  • transmute_int_to_char: Checks for transmutes from an integer to a char.
  • nonminimal_bool: Checks for boolean expressions that can be written more concisely.
  • needless_bool: Checks for expressions of the form if c { true } else { false } (or vice versa) and suggest using the condition directly.
  • misrefactored_assign_op: Checks for a op= a op b or a op= b op a patterns.
  • neg_cmp_op_on_partial_ord: Checks for the usage of negated comparison operators on types which only implement PartialOrd (e.g. f64).
  • zero_prefixed_literal: Warns if an integral constant literal starts with 0.
  • bool_comparison: Checks for expressions of the form x == true (or vice versa) and suggest using the variable directly.
  • extra_unused_lifetimes: Checks for lifetimes in generics that are never used anywhere else.
  • int_plus_one: Checks for usage of x >= y + 1 or x - 1 >= y (and <=) in a block
  • duration_subsec: Checks for calculation of subsecond microseconds or milliseconds from other Duration methods.
  • unnecessary_cast: Checks for casts to the same type.
  • unused_label: Checks for unused labels.
  • result_map_unit_fn: Checks for usage of result.map(f) where f is a function or closure that returns the unit type.
  • clone_on_copy: Checks for usage of .clone() on a Copy type.
  • unit_arg: Checks for passing a unit value as an argument to a function without using a unit literal (()).
  • transmute_int_to_float: Checks for transmutes from an integer to a float.
  • double_comparisons: Checks for double comparisons that could be simplified to a single expression.
  • eval_order_dependence: Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation order of sub-expressions.
  • ref_in_deref: Checks for references in expressions that use auto dereference.
  • mut_range_bound: Checks for loops which have a range bound that is a mutable variable
  • transmute_int_to_bool: Checks for transmutes from an integer to a bool.
  • needless_lifetimes: Checks for lifetime annotations which can be removed by relying on lifetime elision.
  • explicit_counter_loop: Checks for loops over slices with an explicit counter and suggests the use of .enumerate().
  • explicit_write: Checks for usage of write!() / writeln()! which can be replaced with (e)print!() / (e)println!()
  • deref_addrof: Checks for usage of *& and *&mut in expressions.
  • filter_next: Checks for usage of _.filter(_).next().
  • borrowed_box: Checks for use of &Box<T> anywhere in the code.
  • type_complexity: Checks for types used in structs, parameters and let declarations above a certain complexity threshold.
  • match_as_ref: Checks for match which is used to add a reference to an Option value.
  • char_lit_as_u8: Checks for expressions where a character literal is cast to u8 and suggests using a byte literal instead.

perf (Warn)

  • mutex_atomic: Checks for usages of Mutex<X> where an atomic will do.
  • large_enum_variant: Checks for large size differences between variants on enums.
  • manual_memcpy: Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.
  • boxed_local: Checks for usage of Box<T> where an unboxed T would work fine.
  • box_vec: Checks for use of Box<Vec<_>> anywhere in the code.
  • useless_vec: Checks for usage of &vec![..] when using &[..] would be possible.
  • map_entry: Checks for uses of contains_key + insert on HashMap or BTreeMap.
  • cmp_owned: Checks for conversions to owned values just for the sake of a comparison.
  • or_fun_call: Checks for calls to .or(foo(..)), .unwrap_or(foo(..)), etc., and suggests to use or_else, unwrap_or_else, etc., or unwrap_or_default instead.
  • unused_collect: Checks for using collect() on an iterator without using the result.
  • expect_fun_call: Checks for calls to .expect(&format!(...)), .expect(foo(..)), etc., and suggests to use unwrap_or_else instead
  • naive_bytecount: Checks for naive byte counts
  • iter_nth: Checks for use of .iter().nth() (and the related .iter_mut().nth()) on standard library types with O(1) element access.
  • single_char_pattern: Checks for string methods that receive a single-character str as an argument, e.g. _.split("x").

pedantic (Allow)

  • expl_impl_clone_on_copy: Checks for explicit Clone implementations for Copy types.
  • result_map_unwrap_or_else: Checks for usage of result.map(_).unwrap_or_else(_).
  • maybe_infinite_iter: Checks for iteration that may be infinite.
  • cast_possible_wrap: Checks for casts from an unsigned type to a signed type of the same size. Performing such a cast is a 'no-op' for the compiler, i.e. nothing is changed at the bit level, and the binary representation of the value is reinterpreted. This can cause wrapping if the value is too big for the target signed type. However, the cast works as defined, so this lint is Allow by default.
  • cast_sign_loss: Checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values, which can be quite surprising in practice. However, as the cast works as defined, this lint is Allow by default.
  • enum_glob_use: Checks for use Enum::*.
  • match_same_arms: Checks for match with identical arm bodies.
  • single_match_else: Checks for matches with a two arms where an if let will usually suffice.
  • pub_enum_variant_names: Detects enumeration variants that are prefixed or suffixed by the same characters.
  • use_self: Checks for unnecessary repetition of structure name when a replacement with Self is applicable.
  • option_map_unwrap_or_else: Checks for usage of _.map(_).unwrap_or_else(_).
  • items_after_statements: Checks for items declared after some statement in a block.
  • empty_enum: Checks for enums with no variants.
  • needless_continue: The lint checks for if-statements appearing in loops that contain a continue statement in either their main blocks or their else-blocks, when omitting the else-block possibly with some rearrangement of code can make the code easier to understand.
  • string_add_assign: Checks for string appends of the form x = x + y (without let!).
  • used_underscore_binding: Checks for the use of bindings with a single leading underscore.
  • cast_possible_truncation: Checks for on casts between numerical types that may truncate large values. This is expected behavior, so the cast is Allow by default.
  • doc_markdown: Checks for the presence of _, :: or camel-case words outside ticks in documentation.
  • unseparated_literal_suffix: Warns if literal suffixes are not separated by an underscore.
  • if_not_else: Checks for usage of ! or != in an if condition with an else branch.
  • filter_map: Checks for usage of _.filter(_).map(_), _.filter(_).flat_map(_), _.filter_map(_).flat_map(_) and similar.
  • stutter: Detects type names that are prefixed or suffixed by the containing module's name.
  • similar_names: Checks for names that are very similar and thus confusing.
  • replace_consts: Checks for usage of ATOMIC_X_INIT, ONCE_INIT, and uX/iX::MIN/MAX.
  • option_map_unwrap_or: Checks for usage of _.map(_).unwrap_or(_).
  • inline_always: Checks for items annotated with #[inline(always)], unless the annotated function is empty or simply panics.
  • linkedlist: Checks for usage of any LinkedList, suggesting to use a Vec or a VecDeque (formerly called RingBuf).
  • mut_mut: Checks for instances of mut mut references.
  • non_ascii_literal: Checks for non-ASCII characters in string literals.
  • unicode_not_nfc: Checks for string literals that contain Unicode in a form that is not equal to its NFC-recomposition.
  • cast_precision_loss: Checks for casts from any numerical to a float type where the receiving type cannot store all values from the original type without rounding errors. This possible rounding is to be expected, so this lint is Allow by default. Basically, this warns on casting any integer with 32 or more bits to f32 or any 64-bit integer to f64.
  • invalid_upcast_comparisons: Checks for comparisons where the relation is always either true or false, but where one side has been upcast so that the comparison is necessary. Only integer types are checked.

nursery (Allow)

restriction (Allow)

  • integer_arithmetic: Checks for plain integer arithmetic.
  • shadow_reuse: Checks for bindings that shadow other bindings already in scope, while reusing the original value.
  • option_unwrap_used: Checks for .unwrap() calls on Options.
  • assign_ops: Checks for compound assignment operations (+= and similar).
  • shadow_unrelated: Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use the original value.
  • clone_on_ref_ptr: Checks for usage of .clone() on a ref-counted pointer, (Rc, Arc, rc::Weak, or sync::Weak), and suggests calling Clone via unified function syntax instead (e.g. Rc::clone(foo)).
  • wrong_pub_self_convention: This is the same as wrong_self_convention, but for public items.
  • indexing_slicing: Checks for usage of indexing or slicing.
  • float_arithmetic: Checks for float arithmetic.
  • string_add: Checks for all instances of x + _ where x is of type String, but only if string_add_assign does not match.
  • else_if_without_else: Checks for usage of if expressions with an else if branch, but without a final else branch.
  • shadow_same: Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.
  • missing_docs_in_private_items: Warns if there is missing doc for any documentable item (public or private).
  • use_debug: Checks for use of Debug formatting. The purpose of this lint is to catch debugging remnants.
  • mem_forget: Checks for usage of std::mem::forget(t) where t is Drop.
  • unimplemented: Checks for usage of unimplemented!.
  • print_stdout: Checks for printing on stdout. The purpose of this lint is to catch debugging remnants.
  • result_unwrap_used: Checks for .unwrap() calls on Results.
  • multiple_inherent_impl: Checks for multiple inherent implementations of a struct
  • decimal_literal_representation: Warns if there is a better representation for a numeric literal.
  • float_cmp_const: Checks for (in-)equality comparisons on floating-point value and constant, except in functions called *eq* (which probably implement equality for a type involving floats).

Rationale and alternatives

We don't particularly need a 1.0, however it's good to have a milestone here, and a general idea of stability as we move forward in this process.

It's also good to have some community involvement in the lint design/categorization process since Clippy lints both reflect and affect the general style of the community.

Unresolved questions

Through the process of this RFC we hope to determine if there are lints which need to be uplifted, recategorized, or removed.