code-moniker-check 0.7.1

Rules engine for code-moniker: DSL, rule config/profiles, evaluation over the symbol graph, and suppression.
Documentation
# code-moniker default rule pack — embedded into the binary at build time.
# Users override per-project via `.code-moniker.toml` at the repo root.
#
# Each `[[<lang>.<kind>.where]]` entry is a single assertion in the form
# `<lhs> <op> <rhs>`. A def violates the rule when the assertion is false.
# Suppress with `// code-moniker: ignore[<lang>.<kind>.<id>]`.
#
# Embedded defaults stick to structural / naming hygiene that is broadly
# uncontroversial. Project-specific policies (comment-content allow-lists,
# doc-comment requirements, max-lines/max-methods budgets) belong in the
# user overlay — see docs/cli/check-dsl.md for a worked example.

# Aliases factor the case regexes shared across languages. A user overlay
# may redefine any of these to tighten or loosen the convention without
# touching the per-kind rules.
[aliases]
pascal    = "name =~ ^[A-Z][A-Za-z0-9]*$"
camel     = "name =~ ^[a-z_][A-Za-z0-9]*$"
snake     = "name =~ ^[a-z_][a-z0-9_]*$"
screaming = "name =~ ^[A-Z][A-Z0-9_]*$"
go_id     = "name =~ ^([A-Z][a-zA-Z0-9_]*|[a-z][a-zA-Z0-9_]*)$"
ts_callback = "name =~ ^__cb_[0-9]+_[0-9]+$"
# Two qualifiers with a package-shaped second segment are unambiguously a
# package path. One-qualifier forms stay silent because `foo.Bar` and
# `Outer.Inner` cannot be distinguished from source text alone.
java_package_qualified = "text =~ ^[A-Za-z_$][A-Za-z0-9_$]*\\.[a-z_$][A-Za-z0-9_$]*\\."

# ─── TypeScript / JavaScript ──────────────────────────────────────────────
[[ts.class.where]]
id      = "name-pascalcase"
expr    = "$pascal"
message = "Class names must be PascalCase. Rename `{name}`."

[[ts.interface.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[ts.type.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[ts.function.where]]
id      = "name-camelcase"
expr    = "$camel OR $ts_callback"
message = "Function names use camelCase. Rename `{name}`."

[[ts.function.where]]
id      = "no-placeholder-names"
expr    = "name !~ ^(helper|helpers|utils|util|manager|temp|tmp|handleData|processData|newFunction[0-9]*)$"
message = "`{name}` is a placeholder name. Pick a verb that describes the actual behaviour."

# ─── Rust ─────────────────────────────────────────────────────────────────
[[rust.fn.where]]
id   = "name-snakecase"
expr = "$snake"

[[rust.struct.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[rust.trait.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[rust.type.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[rust.enum.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[rust.const.where]]
id   = "name-screaming"
expr = "$screaming"

# ─── Java ─────────────────────────────────────────────────────────────────
[[java.class.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[java.interface.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[java.method.where]]
id   = "name-camelcase"
expr = "$camel"

# Java fields include both regular fields and `static final` constants in the
# extracted graph, so the conservative default accepts both conventions.
[[java.field.where]]
id   = "name-camelcase"
expr = "$camel OR $screaming"

[[java.enum_constant.where]]
id   = "name-screaming"
expr = "$screaming"

[[java.refs.where]]
id       = "no-unnecessary-qualified-type-name"
severity = "warn"
expr     = "kind != 'uses_type' OR NOT $java_package_qualified OR any(source.out_refs, kind = 'imports_symbol' AND target.name = current.target.name AND target != current.target) OR any(source.ancestors.out_refs, kind = 'imports_symbol' AND target.name = current.target.name AND target != current.target)"
message  = "Qualified Java type reference can use simple name `{target.name}` here; keep fully qualified names for real ambiguity."

# ─── Python ───────────────────────────────────────────────────────────────
[[python.function.where]]
id   = "name-snakecase"
expr = "$snake"

[[python.class.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[python.method.where]]
id   = "name-snakecase"
expr = "$snake"

# ─── Go ───────────────────────────────────────────────────────────────────
[[go.type.where]]
id   = "name-go"
expr = "$go_id"

[[go.const.where]]
id   = "name-go"
expr = "$go_id"

# ─── C ───────────────────────────────────────────────────────────────────
[[c.func.where]]
id   = "name-identifier"
expr = "name =~ ^[A-Za-z_][A-Za-z0-9_]*$"

# ─── C# ───────────────────────────────────────────────────────────────────
[[cs.class.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[cs.interface.where]]
id   = "name-i-prefix"
expr = "name =~ ^I[A-Z][A-Za-z0-9]*$"

[[cs.record.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[cs.method.where]]
id   = "name-pascalcase"
expr = "$pascal"

[[cs.property.where]]
id   = "name-pascalcase"
expr = "$pascal"

# ─── SQL / PL/pgSQL ───────────────────────────────────────────────────────
[[sql.function.where]]
id   = "name-snakecase"
expr = "$snake"

[[sql.table.where]]
id   = "name-snakecase"
expr = "$snake"

[[sql.view.where]]
id   = "name-snakecase"
expr = "$snake"

# ─── Cross-language structural invariants ────────────────────────────────
# These use the `shape` projection (see docs/cli/check-dsl.md "Shape — the
# canonical kind grouping") to express rules that hold regardless of
# which language's kinds are involved.

# Annotations have no business calling code or referencing types — they
# are leaf nodes in the graph by construction.
[[refs.where]]
id      = "annotations-do-not-emit-non-annotates-refs"
expr    = "source.shape = 'annotation' => kind = 'annotates'"
message = "An annotation def (`{source.name}`) emitted a `{kind}` ref. The annotation shape is leaf — only `annotates` refs originate from it."