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
BEA001UndefinedNameNamingVariable or function used before being defined.
BEA002DuplicateArgumentFunctionsDuplicate parameter names in a function definition.
BEA003ReturnOutsideFunctionFlowreturn statement outside of a function or method body.
BEA004YieldOutsideFunctionFlowyield or yield from used outside a function context.
BEA005BreakOutsideLoopFlowbreak used outside a for/while loop.
BEA006ContinueOutsideLoopFlowcontinue used outside a for/while loop.
BEA007DefaultExceptNotLastExceptionA bare except: is not the final exception handler in a try block.
BEA008RaiseNotImplementedSemanticsUsing raise NotImplemented instead of raise NotImplementedError.
BEA009TwoStarredExpressionsSyntaxTwo or more * unpacking expressions in assignment.
BEA010TooManyExpressionsInStarredAssignmentSyntaxToo many expressions when unpacking into a starred target.
BEA011IfTupleLogicA tuple literal used as an if condition — always True.
BEA012AssertTupleLogicAssertion always true due to tuple literal.
BEA013FStringMissingPlaceholdersStringsf-string declared but contains no {} placeholders.
BEA014TStringMissingPlaceholdersStringst-string declared but contains no placeholders.
BEA015UnusedImportSymbolsImport is never used within the file.
BEA016UnusedVariableSymbolsLocal variable assigned but never used.
BEA017UnusedAnnotationSymbolsAnnotated variable never referenced.
BEA018RedefinedWhileUnusedNamingVariable redefined before original was used.
BEA019ImportShadowedByLoopVarScopeImport name shadowed by a loop variable.
BEA020ImportStarNotPermittedImportsfrom module import * used inside a function or class.
BEA021ImportStarUsedImportsimport * prevents detection of undefined names.
BEA022UnusedIndirectAssignmentNamingGlobal or nonlocal declared but never reassigned.
BEA023ForwardAnnotationSyntaxErrorTypingSyntax error in forward type annotation.
BEA024MultiValueRepeatedKeyLiteralDictDictionary literal repeats key with different values.
BEA025PercentFormatInvalidFormatStringsInvalid % format string.
BEA026IsLiteralLogicComparing constants with is or is not instead of ==/!=.
BEA027DefaultExceptNotLastExceptionBare except: must appear last.
BEA028UnreachableCodeFlowCode after a return, raise, or break is never executed.
BEA029RedundantPassCleanuppass used in a block that already has content.
BEA030EmptyExceptExceptionexcept: with no handling code (silent failure).
BEA031InconsistentExportImports__all__ exports symbol that is not defined in module.
BEA032ConflictingStubDefinitionTypingConflicting type definitions for the same symbol across stub files.
BEA033UnusedExportImportsExported 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 ArgumentMutableDefaultArgumentSemanticDetect functions that use a mutable object (e.g., list, dict, set) as a default argument.
Return in FinallyReturnInFinallyFlowCatch a return, break, or continue inside a finally block: this often suppresses the original exception and leads to subtle bugs.
For-Else Without BreakForElseWithoutBreakFlowThe 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 CaughtBroadExceptionCaughtExceptionCatching 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 TypesInconsistentReturnTypesFunctionA 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 LikelyUnsafeIndexOrKeyAccessDataDetect patterns that likely lead to IndexError or KeyError, e.g., accessing list/dict without checking length/keys, especially inside loops.
Unused Coroutine / Async FunctionUnusedCoroutineSymbolIn async code: a async def function is defined but neither awaited nor returned anywhere — likely a bug.
Resource Leak / Unclosed DescriptorUnclosedResourceSymbolDetect file or network resource opened (e.g., open(...)) without being closed or managed via context manager (with).
Logging Format String ErrorsLoggingFormatErrorStringUsing % 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 == / !=NoneComparisonLogicDiscourage == None or != None in favor of is None / is not None.