diff options
| -rw-r--r-- | CHANGELOG.md | 12 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 25 |
2 files changed, 30 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c36f25..9863596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ Any contributor making changes to the project must record their changes in this **- Milestone.1 "MVP" -** -- [Release 0.2.0 (Unreleased)](#release-020-unreleased) +- [Release 0.2.1 (Unreleased)](#release-021-unreleased) +- [Release 0.2.0 (2026-06-30)](#release-020-2026-06-30) - [Release 0.1.9 (2026-05-29)](#release-019-2026-05-29) - [Release 0.1.8 (2026-05-18)](#release-018-2026-05-18) - [Release 0.1.7 (2026-05-04)](#release-017-2026-05-04) @@ -26,11 +27,16 @@ Any contributor making changes to the project must record their changes in this ## Contents -### Release ? (Unreleased) +### Release 0.2.1 (Unreleased) #### Fixes: -None +1. **[`macros`]** Fixed false positives in `entry_has_variant` caused by bare substring matching in the third `contains` check. + When a longer variant (e.g., `EntryListAlias`) is registered first, followed by a shorter variant that shares the same prefix (e.g., `EntryList`), + `"=> EntryList"` would incorrectly match as a substring of `"=> EntryListAlias,"`, causing a false duplicate registration detection. + Now changed to use `find` + trailing character boundary validation, ensuring the character immediately after the match is not an identifier character (letter/digit/underscore). + + Affected scope: Deduplication logic for `#[chain]`, `#[renderer]`, `#[help]`, and `#[completion]` registration. #### Optimizations: diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 546416d..c82466d 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -241,10 +241,27 @@ pub(crate) fn check_duplicate_variant( /// Checks if a stored entry string contains the given variant name. /// Handles both "StructName => Variant," and "Self::Variant => ..." formats. fn entry_has_variant(entry: &str, variant_name: &str) -> bool { - entry.contains(&format!("=> {variant_name},")) - || entry.contains(&format!("=> {variant_name} ")) - || entry.contains(&format!("=> {variant_name}")) - || entry.contains(&format!(":: {variant_name} =>")) + let variant_match = format!("=> {variant_name}"); + + // "StructName => Variant," — exact match with trailing comma + if entry.contains(&format!("{variant_match},")) { + return true; + } + // "StructName => Variant " — exact match with trailing space + if entry.contains(&format!("{variant_match} ")) { + return true; + } + // "StructName => Variant" (fallback) — must NOT be followed by identifier chars + if let Some(idx) = entry.find(&variant_match) { + let after = idx + variant_match.len(); + if after >= entry.len() + || !entry[after..].starts_with(|c: char| c.is_alphanumeric() || c == '_') + { + return true; + } + } + // "Self::Variant => ..." — match-arm existence check format + entry.contains(&format!(":: {variant_name} =>")) } /// Registers an outside-type as a member of a program group without modifying its definition. |
