Read the `raise` effect annotation, and `guard!`, in the parser

`parse_fn_signature` expected `{` straight after `-> T` and did not know
`raise`, so every signature carrying an effect annotation died with
"expected '{'" at the body -- the parser reporting a token it does not know
rather than a real syntax error. Settled against `moon check` first: all four
forms (`-> T raise`, `-> T raise E`, `-> T!`, `-> Unit raise E`) give zero
errors, and the bare form is taken verbatim from
`moonbitlang/core/error/error_test.mbt`, so it is a corpus shape rather than
one invented here.

One clause, TWO positions. A function type may carry its own effect
annotation too -- `fn f(g : () -> Int raise)` also settles to zero errors --
but the parser reported "expected ')'" and lost the rest of the file. Fixing
only the signature would leave a half-fix that knows `raise` in one position
and not the other, and proton's 33 files plus async's 10 are exactly that
shape. Both call sites now share `Parser::parse_raise_effect`.

The effect TYPE is consumed but deliberately not recorded. Nothing in walk /
types / infer / interp reads one, and adding a field to `SFunc` for a check
that does not exist would ripple through all four plus the `.mbti`. Consuming
it is what makes a file readable; storing it is a separate decision.

The "a bare `raise` must stop here" guard asks "can the next token begin a
type at all" (TName / TLParen / TAmp), not "which tokens terminate", because
the two directions are not symmetric: reading one token too many is a SILENT
misparse (the body gets swallowed into the type annotation and nothing
reports), while stopping one token early is a loud "expected '{'".

Second defect, found while re-scanning: `guard!` is `guard` plus a `!`, and
the lexer does not join them. `parse_guard` bumped the name and went straight
to the condition, so the `!` fell through to the expression reader and became
a boolean NOT applied to the condition -- a confidently worded false positive
about code that compiles:

    // moonbitlang/async 0.22.4 src/internal/fd_util/pipe.mbt:31
    guard! fds is [r, w]
    // reported: operand of '!' must be bool, got '[int]'

The defect was always there; "the file did not parse at all" was hiding it.
The raise fix only stopped covering for it. So TBang has three owners in one
file -- the raise short form, `guard!`, and real boolean negation -- and all
three are decided BY POSITION. Three control tests pin one each.

CORRECTION to a number reported earlier. The "149 of moui's 608 files, 24.5%
carry `-> T raise`" figure counted vendored `.mooncakes`, which the scanner
does not scan. Excluding vendored trees moui has 2 such files. The same ruler
over the six corpora: async 72 signature + 10 type position, proton 70 + 33,
moonbit_image 14, proton_contract 2, moonbit_labeler 2 + 1, moui 2. The fix is
real -- `moonbit_image/bmp.mbt:26`'s `-> Image raise Failure` now parses
cleanly -- but the scale was reported an order of magnitude too large. Readable
files across the six corpora go 787 -> 825.

The cost, stated plainly: readable files rose and so did actionable, because
every newly readable file contributes findings and some of them are wrong.
Counted by family, every new actionable finding is `UndefinedName` (proton
43 -> 103, async 17 -> 22, moonbit_image 0 -> 3); `UnusedParam` is unchanged
and `OperatorMismatch` went DOWN (2 -> 0) once `guard!` was fixed. Sampling
confirms the false positives: proton's `OperationFailed` is a constructor of
`WindowSessionError`, used that way in four places across the corpus, but its
`enum`/`suberror`/`derive` declaration is outside the analyzed subset so its
body is never analysed and the name never enters the symbol table. That cause
is the pre-existing subset boundary, not this parse change -- this release
just made those files readable for the first time. moui actionable stays 1.

Mutation gate: 9 mutations, 9 killed, 0 survived, every restore verified
byte-identical. Three results worth keeping:

  - M7/M8 are the same guard in opposite directions. Admit everything and a
    bare `raise` swallows the body's `{` (silent misparse); admit nothing and
    every named effect stops being consumed. Testing one direction alone lets
    "too strict" pass for "correct".
  - What M5 killed was not `skip_indexed()`, it was the `raise` keyword
    itself. Removing `skip_indexed()` came back 371/371 green -- an
    equivalent mutation, because `type_annotation` already calls
    `indexed_suffix()` on the name it reads, so `raise E[Int]` is consumed
    whole without it. The dead call is deleted and the reason is recorded at
    the call site: keeping it made the reader look responsible for a case it
    is not.
  - M2's first result was COMPILE-ERR, which was a bug in the gate rather than
    a result: the Find string began with a newline, the replacement merged into
    the preceding comment line, and the mutation never ran. The only reliable
    criterion is the absence of a `Total tests:` line, never a grep.

Two gaps measured and deliberately left for the next release:

  - The declared return type renders as `None` when the body ends in a tail
    expression: `fn f() -> Int { 1 }` gives `f() -> None` while `fn f() -> Int
    { return 1 }` gives `f() -> int`. Nothing to do with `raise` -- all four
    forms behave identically with and without an explicit `return`. Real
    MoonBit leans on tail expressions, so this probably costs more than the
    raise clause did. The 0.4.23 tests therefore deliberately use an explicit
    `return`, or the assertion would go red for an unrelated reason.
  - A parenthesised function type in return position is read as a tuple:
    `fn f() -> (() -> Int) {` reports "expected '{'", with no `raise`
    involved. A real `-> (Int, String)` is fine. Small corpus cost: 6 files in
    moui, 0 elsewhere.

Tests: 374 js / 446 wasm / 374 wasm-gc (14 new). moon check and moon fmt
--check clean.
