feat(parser): peel a leading supertype off "<Supertype> <Subtype>" subjects#5549
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
matthewevans
left a comment
There was a problem hiding this comment.
✅ Architecturally clean, correct building-block fix — holding formal approval for CI.
✅ Clean
- Right seam. The supertype-peel lives in
typed_filter_for_subtype(grammar.rs), the single subtype-filter authority, so every<supertype> <subtype>compound benefits — not just the two cards cited. Recursing intotyped_filter_for_subtype(rest)composes cleanly rather than special-casing. - Reuses the existing combinator.
split_leading_supertypedelegates tonom_target::parse_supertype_wordinstead of hand-rolling supertype matching; the original-case remainder uses the documenteddescriptor[len − rest_lower.len()..]offset idiom. - CR + cards verified. CR 205.4a grep-confirmed verbatim ("supertypes are basic, legendary, ongoing, snow, and world"); CR 205.3m confirms Human/Snake are subtypes. General's Enforcer — "Legendary Humans you control have indestructible." — and Kashi-Tribe Elite — "Legendary Snakes you control have shroud." — both match Scryfall. This is the fabricated zero-match
Subtype("Legendary Human")class fixed at the source. - Discriminating tests at both levels: the building-block test asserts the peel plus a plain-
Goblinnegative (no fabricated supertype), and the integration test locksHasSupertype(Legendary)+Subtype(Human)+ the anthemAddKeywordmodification.
Recommendation: approve as bug once CI settles green on a rebased head (currently BEHIND, CI pending). No changes requested.
Parse changes introduced by this PR · 2 card(s), 2 signature(s) (baseline: main
|
…bjects
CR 205.4a + CR 205.3m: A subject phrase like "Legendary Humans you control"
names the legendary supertype plus the Human subtype, but the shared subtype
authority `typed_filter_for_subtype` treated the whole two-word string as one
subtype and fabricated `Subtype("Legendary Human")` — a filter that matches no
card in existence. So General's Enforcer ("Legendary Humans you control have
indestructible") and Kashi-Tribe Elite ("Legendary Snakes you control have
shroud") silently applied their anthem to nothing.
Fix at the single authority: `typed_filter_for_subtype` now peels a leading
supertype word ("Legendary"/"Snow"/"Basic"/...) via a new `split_leading_supertype`
helper, attaching it as a `FilterProp::HasSupertype` and resolving the remainder
("Human", "Snake") as the real subtype. Because every capitalized-subtype
fallback in the subject parsers routes through this one function, all four call
sites (`parse_typed_you_control`'s creature/combat-prefix arms and the
`parse_creature_subject_filter` fallback) inherit the fix. A bare supertype
("Legendary creatures") still routes through its own path unchanged, and a plain
subtype ("Goblins") gains no supertype property.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…combinator gate)
`split_leading_supertype` peeled the supertype word with a nom combinator but
then consumed the separating space with `strip_prefix(' ')`, tripping the parser
combinator gate. Consume the word and its trailing space atomically via
`terminated(parse_supertype_word, tag(" "))` — a bare supertype (no following
subtype) now fails the trailing space and declines, identical to the prior
`strip_prefix(' ')?` behavior, with no string-method dispatch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
c15df99 to
f4439c7
Compare
matthewevans
left a comment
There was a problem hiding this comment.
✅ Approving — CI resolved green, merge state CLEAN. Correctness verified in the prior review (CR 205.4a supertype handling at the single subtype/type authority, reuses parse_supertype_word, discriminating tests). The head that went green is the reviewed head; enqueuing as bug.
CR 205.4a + CR 205.3m
Bug
A subject phrase like "Legendary Humans you control" names the legendary supertype plus the Human subtype — but the shared subtype authority
typed_filter_for_subtypetreated the whole two-word string as one subtype and fabricatedSubtype("Legendary Human"), a filter that matches no card in existence. The anthems therefore applied to nothing at runtime:Legendary Humans you control have indestructible.Legendary Snakes you control have shroud.Before:
affected = [Creature, Subtype("Legendary Human")](zero matches).After:
affected = [Creature, Subtype("Human")] + HasSupertype(Legendary).Fix
At the single authority
typed_filter_for_subtype(grammar.rs): peel a leading supertype word ("Legendary"/"Snow"/"Basic"/…) via a newsplit_leading_supertypehelper, attach it asFilterProp::HasSupertype, and resolve the remainder as the real subtype (recursing so the core-type inference still runs on the true subtype).Because every capitalized-subtype fallback in the subject parsers routes through this one function, all four call sites —
parse_typed_you_control's creature-arm and combat-status-prefix arm, plus theparse_creature_subject_filterfallback — inherit the fix from one place.Why it's the right seam / minimal blast radius
<supertype> <subtype>subject class is covered in a single change.descriptor_is_supertype) and is untouched; a plain subtype ("Goblins") gains no supertype property (regression-tested).split_leading_supertypereturnsNonefor a bare supertype (no following subtype), so it never strips a legitimate lone supertype.Tests
legendary_subtype_subject_peels_supertype_and_keeps_subtype— end-to-end viaparse_static_line: General's Enforcer →Subtype("Human")+HasSupertype(Legendary)+AddKeyword(Indestructible).typed_filter_for_subtype_peels_leading_supertype— building-block: "Legendary Human" → Human + HasSupertype(Legendary); "Goblin" → Goblin, no supertype (regression).parser::oracle_static::tests(1039 passed, 0 failed), including the existing bare-supertype "Legendary creatures" / Jodah tests.CR verification
docs/MagicCompRules.txt.AI-contributor notes
<supertype> <subtype>subject (current or future) is covered.cargo fmt, CI-exactclippy -p engine --all-targets --features proptest -D warnings, and the full oracle_static test module are green.🤖 Generated with Claude Code