Beacon Linter

The Beacon Rule Engine is a modular static analysis system powering diagnostics in Beacon.

It's foundationally a pure Rust implementation of PyFlakes.

Suppressing Warnings

Individual linter warnings can be suppressed using inline comments:

import os  # noqa: BEA015  # Suppress unused import warning
x = undefined  # noqa  # Suppress all warnings on this line

See Suppressions for complete documentation on suppression comments.


Legend: ⚠ = Warning ✕ = Error ⓘ = Info

CodeName / RuleKindLevelCategoryDescription
BEA001UndefinedName✕NamingVariable or function used before being defined.
BEA002DuplicateArgument✕FunctionsDuplicate parameter names in a function definition.
BEA003ReturnOutsideFunction✕Flowreturn statement outside of a function or method body.
BEA004YieldOutsideFunction✕Flowyield or yield from used outside a function context.
BEA005BreakOutsideLoop✕Flowbreak used outside a for/while loop.
BEA006ContinueOutsideLoop✕Flowcontinue used outside a for/while loop.
BEA007DefaultExceptNotLast⚠ExceptionA bare except: is not the final exception handler in a try block.
BEA008RaiseNotImplemented⚠SemanticsUsing raise NotImplemented instead of raise NotImplementedError.
BEA009TwoStarredExpressions✕SyntaxTwo or more * unpacking expressions in assignment.
BEA010TooManyExpressionsInStarredAssignment✕SyntaxToo many expressions when unpacking into a starred target.
BEA011IfTuple⚠LogicA tuple literal used as an if condition — always True.
BEA012AssertTuple⚠LogicAssertion always true due to tuple literal.
BEA013FStringMissingPlaceholders⚠Stringsf-string declared but contains no {} placeholders.
BEA014TStringMissingPlaceholders⚠Stringst-string declared but contains no placeholders.
BEA015UnusedImport⚠SymbolsImport is never used within the file.
BEA016UnusedVariable⚠SymbolsLocal variable assigned but never used.
BEA017UnusedAnnotation⚠SymbolsAnnotated variable never referenced.
BEA018RedefinedWhileUnused⚠NamingVariable redefined before original was used.
BEA019ImportShadowedByLoopVar⚠ScopeImport name shadowed by a loop variable.
BEA020ImportStarNotPermitted✕Importsfrom module import * used inside a function or class.
BEA021ImportStarUsed⚠Importsimport * prevents detection of undefined names.
BEA022UnusedIndirectAssignment⚠NamingGlobal or nonlocal declared but never reassigned.
BEA023ForwardAnnotationSyntaxError✕TypingSyntax error in forward type annotation.
BEA024MultiValueRepeatedKeyLiteral⚠DictDictionary literal repeats key with different values.
BEA025PercentFormatInvalidFormat⚠StringsInvalid % format string.
BEA026IsLiteral⚠LogicComparing constants with is or is not instead of ==/!=.
BEA027DefaultExceptNotLast⚠ExceptionBare except: must appear last.
BEA028UnreachableCode⚠FlowCode after a return, raise, or break is never executed.
BEA029RedundantPassⓘCleanuppass used in a block that already has content.
BEA030EmptyExcept⚠Exceptionexcept: with no handling code (silent failure).
BEA031InconsistentExport⚠Imports__all__ exports symbol that is not defined in module.
BEA032ConflictingStubDefinition⚠TypingConflicting type definitions for the same symbol across stub files.
BEA033UnusedExport⚠ImportsExported function or class is never used across the workspace.

Rules

BEA001

Example

print(foo) before foo is defined.

Fix

Define the variable before use or fix the typo.

BEA002

Example

def f(x, x):
    pass

Fix

Rename one of the parameters.

BEA003

Example

Top-level return 5 in a module.

Fix

Remove or move inside a function.

BEA004

Example

yield x at module scope.

Fix

Wrap in a generator function.

BEA005

Example

break in global scope or in a function without loop.

Fix

Remove or restructure the code to include a loop.

BEA006

Example

continue in a function with no loop.

Fix

Remove or replace with control flow logic.

BEA007

Example

except: followed by except ValueError:

Fix

Move the except: block to the end of the try.

BEA008

Example

raise NotImplemented

Fix

Replace with raise NotImplementedError.

BEA009

Example

a, *b, *c = d

Fix

Only one starred target is allowed.

BEA010

Example

a, b, c, d = (1, 2, 3)

Fix

Adjust unpacking count.

BEA011

Example

if (x,):
    ...

Fix

Remove accidental comma or rewrite condition.

BEA012

Example

assert (x, y)

Fix

Remove parentheses or fix expression.

BEA013

Example

f"Hello world"

Fix

Remove the f prefix if unnecessary.

BEA014

Example

t"foo"

Fix

Remove the t prefix if unused.

BEA015

Example

import os not referenced.

Fix

Remove the unused import.

BEA016

Example

x = 10 never referenced again.

Fix

Remove assignment or prefix with _ if intentional.

BEA017

Example

x: int declared but unused.

Fix

Remove or use variable.

BEA018

Example

x = 1; x = 2 without reading x.

Fix

Remove unused definition.

BEA019

Example

import os
for os in range(3):
    ...

Fix

Rename loop variable.

BEA020

Example

def f():
    from math import *

Fix

Move import to module level.

BEA021

Example

from os import *

Fix

Replace with explicit imports.

BEA022

Example

global foo never used.

Fix

Remove redundant declaration.

BEA023

Example

def f() -> "List[int": ...

Fix

Fix or quote properly.

BEA024

Example

{'a': 1, 'a': 2}

Fix

Merge or remove duplicate keys.

BEA025

Example

"%q" % 3

Fix

Correct format specifier.

BEA026

Example

x is 5

Fix

Use ==/!=.

BEA027

Example

As above.

Fix

Reorder exception handlers.

BEA028

Example

return 5; print("unreachable")

Fix

Remove or refactor code.

BEA029

Example

def f():
    pass
    return 1

Fix

Remove redundant pass.

BEA030

Example

try:
    ...
except:
    pass

Fix

Handle exception or remove block.

BEA031

Example

def foo():
    pass

def bar():
    pass

__all__ = ["foo", "baz"]  # "baz" is not defined

Fix

Either define the missing symbol or remove it from __all__:

def foo():
    pass

def bar():
    pass

def baz():  # Define the missing symbol
    pass

__all__ = ["foo", "baz"]

Or:

def foo():
    pass

def bar():
    pass

__all__ = ["foo"]  # Remove "baz" from exports

BEA032

Example

When multiple stub files define the same module with conflicting types:

stubs1/mymodule.pyi:

def process(data: int) -> str: ...

stubs2/mymodule.pyi:

def process(data: str) -> int: ...

Fix

Resolve the conflict by ensuring consistent type signatures across all stub files. Remove duplicate stub definitions or consolidate them into a single authoritative stub file.

BEA033

Example

When a function or class is exported via __all__ but never imported or used anywhere in the workspace:

mylib/utils.py:

def public_helper():
    pass

def unused_export():
    # This function is exported but never used in the workspace
    pass

__all__ = ["public_helper", "unused_export"]

main.py:

from mylib.utils import public_helper

# Only public_helper is used, unused_export is never imported
public_helper()

Fix

Remove the unused export from __all__ if it's truly not needed:

def public_helper():
    pass

def unused_export():
    # Make it private or remove from __all__
    pass

__all__ = ["public_helper"]  # Removed unused_export

Or if it's a public API that should be kept, suppress the warning with a comment explaining why it's part of the public interface.

Planned

NameKindCategorySeverityRationale
Mutable Default ArgumentMutableDefaultArgumentSemantic✕Detect functions that use a mutable object (e.g., list, dict, set) as a default argument.
Return in FinallyReturnInFinallyFlow✕Catch a return, break, or continue inside a finally block: this often suppresses the original exception and leads to subtle bugs.
For-Else Without BreakForElseWithoutBreakFlow⚠The for ... else construct where the else never executes a break is confusing and often mis-used. If you have else: on a loop but never break, you may signal confusing logic.
Wrong Exception CaughtBroadExceptionCaughtException⚠Catching too broad exceptions (e.g., except Exception: or except:) instead of specific types can hide bugs. You already have empty except; this expands to overly broad catching.
Inconsistent Return TypesInconsistentReturnTypesFunction⚠A function that returns different types on different paths (e.g., return int in one branch, return None in another) may lead to consuming code bugs especially if not annotated.
Index / Key Errors LikelyUnsafeIndexOrKeyAccessData⚠Detect patterns that likely lead to IndexError or KeyError, e.g., accessing list/dict without checking length/keys, especially inside loops.
Unused Coroutine / Async FunctionUnusedCoroutineSymbol⚠In async code: a async def function is defined but neither awaited nor returned anywhere — likely a bug.
Resource Leak / Unclosed DescriptorUnclosedResourceSymbol⚠Detect file or network resource opened (e.g., open(...)) without being closed or managed via context manager (with).
Logging Format String ErrorsLoggingFormatErrorString⚠Using % or f-string incorrectly in logging calls (e.g., logging format mismatches number of placeholders) can cause runtime exceptions or silent failures.
Comparison to None Using == / !=NoneComparisonLogic⚠Discourage == None or != None in favor of is None / is not None.