Use Walrus Operator in if statementHas Fix
Description
The walrus operator (:=, Python 3.8+) merges an assignment into an expression. This rule merges an assignment to a simple name with the immediately following if that tests it. Apply both rules in one scan of the original code: one rewrites only the condition, preserving the body and any elif or else branches, and the other removes the assignment.
Apply only when all hold: a single assignment to a simple name (such as title), assignment immediately before the if (adjacent), RHS (right-hand side) is pure (no side effects), test is a bare truth check. Otherwise skip - see non-cases below. The rules do not check for side effects; review the RHS before applying them.
YAML
id: use-walrus-operator
language: python
rule:
pattern: $VAR
kind: identifier
inside:
kind: if_statement
field: condition
follows:
pattern:
context: $VAR = $EXPR
selector: expression_statement
stopBy: neighbor
constraints:
VAR:
kind: identifier
EXPR:
not:
kind: assignment
fix: $VAR := ($EXPR)
---
id: remove-declaration
language: python
rule:
pattern:
context: $VAR = $EXPR
selector: expression_statement
precedes:
kind: if_statement
has:
field: condition
pattern: $VAR
kind: identifier
stopBy: neighbor
constraints:
VAR:
kind: identifier
EXPR:
not:
kind: assignment
fix: ''The constraints restrict $VAR to a simple name and exclude chained assignments such as a = b = value. Both rules use the same constraints. Parenthesizing $EXPR preserves tuple expressions such as a = 1, 2.
Negated tests (separate rule)
if not X needs parentheses. The rules above never match it; hand-applying their shape flips the branch. Example:
# before
def get_title(data):
title = (data.get('title') or '').strip()
if not title:
return fallback
validate(title)
return title
# wrong — branch inverted
def get_title(data):
if title := (data.get('title') or '').strip():
return fallback
validate(title)
return titleCorrect form is if not (title := ...):. Apply both rules below together so the RHS is evaluated only once.
id: use-walrus-operator-not
language: python
rule:
pattern: not $VAR
inside:
kind: if_statement
field: condition
follows:
pattern:
context: $VAR = $EXPR
selector: expression_statement
stopBy: neighbor
constraints:
VAR:
kind: identifier
EXPR:
not:
kind: assignment
fix: not ($VAR := ($EXPR))
---
id: remove-declaration-not
language: python
rule:
pattern:
context: $VAR = $EXPR
selector: expression_statement
precedes:
kind: if_statement
has:
field: condition
pattern: not $VAR
stopBy: neighbor
constraints:
VAR:
kind: identifier
EXPR:
not:
kind: assignment
fix: ''Example
a = foo()
if a:
do_bar()Diff
a = foo()
if a:
if a := (foo()):
do_bar()Do not apply when
- The target is an attribute, subscript, or unpacking pattern (for example,
obj.title,items[0], ora, b). Walrus targets must be simple names. - The assignment is chained (for example,
a = b = value) or annotated (for example,a: str = value). These rules skip both. - Code sits between assignment and
if(reordered or skipped). - RHS has side effects; this example assumes a pure expression and does not verify purity.
- The test needs input validation first (for example, external data): truthy does not mean usable. This rewrite preserves the truth test and does not add validation.
Contributed by
Inspired by reddit user /u/jackerhack