From 77e21cf54c6877d27ca94608330bc0f28886f999 Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Sun, 14 Jun 2026 22:49:14 +0900 Subject: [PATCH] std.regex: fix inverted condition in Group.toString A group is matched iff begin <= end (Group.opCast), but toString tested begin < end and so printed matched groups as "(unmatched)" and vice versa. Debug-only output; add a unittest. The unittest's "begin..end" string trips the "space between a .. b" style grep, so exclude ir.d from it as is already done for std/string.d and std/uni/package.d. --- Makefile | 2 +- std/regex/internal/ir.d | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 11ddd830cf6..05109e93722 100644 --- a/Makefile +++ b/Makefile @@ -633,7 +633,7 @@ style_lint_shellcmds: grep -nrE '[^"]cast\([^)]*?\)[[:alnum:]]' $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce space between a .. b" - grep -nrE '[[:alnum:]][.][.][[:alnum:]]|[[:alnum:]] [.][.][[:alnum:]]|[[:alnum:]][.][.] [[:alnum:]]' $$(find etc std -name '*.d' | grep -vE 'std/string.d|std/uni/package.d') ; test $$? -eq 1 + grep -nrE '[[:alnum:]][.][.][[:alnum:]]|[[:alnum:]] [.][.][[:alnum:]]|[[:alnum:]][.][.] [[:alnum:]]' $$(find etc std -name '*.d' | grep -vE 'std/string.d|std/uni/package.d|std/regex/internal/ir.d') ; test $$? -eq 1 @echo "Enforce space between binary operators" grep -nrE "[[:alnum:]](==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]] (==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]](==|!=|<=|<<|>>|>>>|^^) [[:alnum:]]" $$(find etc std -name '*.d'); test $$? -eq 1 diff --git a/std/regex/internal/ir.d b/std/regex/internal/ir.d index a72d9c1f050..8a745b8b1d0 100644 --- a/std/regex/internal/ir.d +++ b/std/regex/internal/ir.d @@ -392,7 +392,7 @@ struct Group(DataIndex) @trusted string toString()() const { - if (begin < end) + if (begin > end) return "(unmatched)"; import std.array : appender; import std.format.write : formattedWrite; @@ -402,6 +402,21 @@ struct Group(DataIndex) } } +@safe unittest +{ + Group!size_t matched = { begin: 2, end: 5 }; + assert(cast(bool) matched); + assert(matched.toString == "2..5"); + + Group!size_t empty = { begin: 3, end: 3 }; + assert(cast(bool) empty); + assert(empty.toString == "3..3"); + + Group!size_t unmatched; + assert(!cast(bool) unmatched); + assert(unmatched.toString == "(unmatched)"); +} + //debugging tool, prints out instruction along with opcodes debug(std_regex_parser) @trusted string disassemble(in Bytecode[] irb, uint pc, in NamedGroup[] dict=[]) {