Lints¶
The Gossamer linter ships 50 day-one checks. Each has a short
identifier suitable for #[lint(allow(...))] and a long-form
explanation available via gos lint --explain <id>. This page
is auto-generated from gossamer-lint; hand edits are
overwritten on the next run of cargo xtask docs-lints.
unused_variable¶
Declares a let binding whose name is never read.
Prefix the name with _ to silence explicitly (e.g. _tmp)
when the binding is intentional but unused.
unused_import¶
A use declaration whose imported name is never referenced.
Remove the import or reference the name in the file.
unused_mut_variable¶
A binding marked mut that is never reassigned. Drop the mut
keyword.
needless_return¶
return expr at the tail of a block is the same as writing expr
by itself. Prefer the tail form for symmetry with the rest of
the expression language.
needless_bool¶
if cond { true } else { false } is the same as cond. The
inverted form is !cond.
comparison_to_bool_literal¶
x == true reads worse than x, and x == false reads worse
than !x. Drop the literal.
single_match¶
A match with a single arm reads better as if let PATTERN = ....
Single-arm match is almost always a half-written exhaustive match.
shadowed_binding¶
A let binding in the same block shadows an earlier one. Rename
one of them to make the data flow obvious.
unchecked_result¶
let _ = expr? silently discards an error. Either handle the
Err branch explicitly or propagate with ? so the caller sees it.
empty_block¶
An empty {} block is almost always a mistake. Add an explicit
() tail if the block is intentional.
panic_in_main¶
panic! inside main aborts without a clean exit code. Return a
Result from main and use ? so the error propagates.
redundant_clone¶
Calling .clone() on a literal or already-copied value is
redundant. Drop the call.
double_negation¶
!!x collapses to x when x: bool. If the double negation is
intentional for truthiness coercion, use an explicit cast.
self_assignment¶
Assigning a variable to itself does nothing. The statement is usually the residue of a refactor — remove it.
todo_macro¶
todo!() and unimplemented!() are placeholders, not shippable
expressions. Implement the branch before merging.
bool_literal_in_condition¶
if true { ... } / while false { ... } — the branch is
decided at compile time. Drop the control-flow construct.
let_and_return¶
let x = expr; x at the tail of a block is just expr. Drop
the needless binding.
collapsible_if¶
if a { if b { ... } } can be combined into
if a && b { ... }. Easier to scan.
if_same_then_else¶
Both branches of the if are identical. Drop the branch and
keep the body once.
redundant_field_init¶
Foo { x: x } is the same as the shorthand Foo { x }.
needless_else_after_return¶
if cond { return X } else { Y } — the else is unreachable
fall-through. Un-nest the else body.
self_compare¶
Comparing a value to itself is always true (for ==, <=,
>=) or false (for !=, <, >). Use the constant.
identity_op¶
x + 0, x - 0, x * 1, x / 1 all equal x. The operation
adds nothing but noise.
unit_let¶
let x = () binds the unit value, which is almost never useful.
Drop the let.
float_eq_zero¶
Equality against a float literal is almost never what you want —
floating-point arithmetic rarely produces the exact bit pattern.
Compare (x - y).abs() < eps with an explicit tolerance.
empty_else¶
else {} adds no information. Drop the else and let the if
stand alone.
match_bool¶
match b { true => ... false => ... } is an if in disguise.
Rewrite as if b { ... } else { ... }.
needless_parens¶
(x) without a trailing comma is a needless pair of parens —
x reads the same. (x,) is a one-tuple and means something
different.
manual_not_equal¶
!(a == b) is just a != b. Prefer the direct operator.
nested_ternary_if¶
Three or more nested if / else if layers are hard to skim.
Rewrite as match on the discriminant.
absurd_range¶
A literal range whose lower bound exceeds its upper bound is empty. Swap the bounds or double-check the intent.
string_literal_concat¶
"a" + "b" can be written directly as "ab". Let the
source reflect the final value.
chained_negation_literals¶
-(-x) is x. The extra unary does nothing.
if_not_else¶
if !cond { A } else { B } scans better as if cond { B }
else { A }. Flip the branches and drop the !.
empty_string_concat¶
Concatenating an empty string literal is a no-op. Drop the
"" + or + "".
println_newline_only¶
println("") already writes a newline. Don't pass "\n"
and don't call it twice to emit a blank line.
match_same_arms¶
Two match arms share the same body. Either collapse them with
| alternation or extract the shared body into a helper.
manual_swap¶
Three consecutive statements let tmp = a; a = b; b = tmp swap
two bindings via a temporary. Prefer a destructuring assignment
once the language supports it, or at minimum document why the
swap is needed.
consecutive_assignment¶
Two back-to-back assignments to the same place — the earlier value is dead before it's read. Drop the first or consolidate the logic into one statement.
large_unreadable_literal¶
Integer literals of five or more digits are easier to scan with
_ as thousands separators: 1_000_000 instead of 1000000.
redundant_closure¶
|x| f(x) is a closure that just forwards to f. Pass f
directly.
empty_if_body¶
An if cond { } else { body } is the same as if !cond { body }.
Invert the condition and drop the empty branch.
bool_to_int_match¶
match b { true => 1, false => 0 } is an if in disguise that
happens to return an integer. Prefer if b { 1 } else { 0 }.
fn_returns_unit_explicit¶
fn f() -> () { ... } is the same as fn f() { ... }. The
explicit -> () is noise.
let_with_unit_type¶
let _: () = expr annotates the binding with the unit type. If
expr was going to return () anyway, the annotation is noise.
If it wasn't, the annotation forces a coercion — use a plain
statement instead.
useless_default_only_match¶
match x { _ => expr } always runs expr — the match adds
nothing. Drop the match (and add let _ = x if evaluating
the scrutinee has side effects).
unnecessary_parens_in_condition¶
if (cond) { ... } wraps the condition in a single-tuple
expression. Drop the parens: if cond { ... }.
pattern_matching_unit¶
match () { ... } has exactly one reachable arm. Drop the match
and run the body directly.
panic_without_message¶
panic() with no argument leaves the post-mortem with nothing
to render. Always pass a brief explanation.
empty_loop¶
loop {} with no body busy-waits forever at 100% CPU. Add a
break, a continue, or replace with a real wait primitive.