We had this only as a repent-only maintenance scribe: `judge` never mentioned it,
and it fired solely on OBJECT types yielded by `new X` or `X::method()`. Your case
— `fn (): string => $this->name` on a string property — was invisible to it.
Now a detector + Repentable scribe, and the proof is the point. `Ast\Support\
ExpressionType` answers "what does this expression PROVABLY yield", spelled as a
declaration spells it: literals, `new X`, a property's declared type (promoted
included), a method's declared return, `self`/`static` resolved. It is the sibling
of TypeResolver, which resolves a receiver chain to a class and by design knows
only class names — which is why it returns null for `string` and could never see
this. The flag fires only when that answer matches the declaration character for
character, nullability included, so a WIDENING (`fn (): ?string` over a `string`
property) survives, and anything computed — a ternary, a coalesce, a call on an
inferred receiver — is ambiguous and stays untouched. Ambiguous is exactly when a
type is worth writing.
`Writer::removeReturnType()` is the one place a declared return comes off, and the
old `RedundantReturnTypeScribe` retires: everything it did, this does, plus scalars.
Calibrated: 0 in laravel/framework, 0 in supplier-compare, 1 in prep-labels, 55 in
smart-farmers, 77 in workflows, 66 here — every shape read by eye. Ours are swept
in this commit (45 files), which also strips `: bool` from our own `where(fn (…):
bool => …)` predicates: provable, therefore redundant by the rule, and the DSL
reflects on the PARAMETER type, so nothing changes but the reading.