# Structural patterns across languages

> Match code *structure* -- not text -- so the same rule catches a real `panic()` call in Go, TypeScript, Rust, or Python without flagging the word in a string or comment.

A regex on `panic(` flags the call inside strings and comments too, and [StyleRule](../../reference/primitives.md#captain_hook.style.StyleRule) matchers only understand Python's AST. The [`Pattern`](../../docs/reference/index.md) condition matches structure through [ast-grep](https://ast-grep.github.io), so `panic($$$)` catches a real call however its arguments are spelled, and never a string that happens to contain the word.

``` python
"""Warn on Go panic() calls so callers return errors instead of aborting the process."""

from __future__ import annotations

from captain_hook import Allow, Event, Input, Pattern, SourceEdits, Warn, hook

hook(
    Event.PostToolUse,
    only_if=[SourceEdits(lang="go"), Pattern("panic($$$)")],
    message="panic() aborts the whole process. Return an error so callers can recover; "
    "reserve panic for truly unrecoverable programmer mistakes.",
    tests={
        Input(tool="Edit", file="server/handler.go", content='func h() {\n\tpanic("boom")\n}\n'): Warn(pattern="error"),
        Input(tool="Edit", file="server/handler.go", content="func h() error {\n\treturn err\n}\n"): Allow(),
    },
)
```


# What it catches

``` go
func h() {
    panic("boom")  // panic() aborts the process; return an error instead
}
```


# What it allows

``` go
func h() error {
    return err  // returns an error for the caller to recover from
}
```

*The catch / allow split mirrors the hook inline `tests`, so it stays true as the hook evolves.*


# Run it yourself

``` bash
uvx capt-hook --hooks docs/examples test
```


# See also

- [Conditions guide](../../docs/guide/primitives.md#filter-with-conditions)
- [Style rules guide](../../docs/examples/styleguide.md)
- [Conditions reference](../../docs/reference/conditions.md)
