Skip to content

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 ​

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:

python
# 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 title

Correct form is if not (title := ...):. Apply both rules below together so the RHS is evaluated only once.

yaml
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 ​

python
a = foo()
if a:
    do_bar()

Diff ​

python
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], or a, 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

Made with ❤️ with Rust