X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/67cdf558540fcb50072632cb50aa953c0583f350..ab9a4aa72166b26d6c3556107c19ba08ee956a88:/t/re/reg_mesg.t diff --git a/t/re/reg_mesg.t b/t/re/reg_mesg.t index b4ba410..0763be0 100644 --- a/t/re/reg_mesg.t +++ b/t/re/reg_mesg.t @@ -1,5 +1,7 @@ #!./perl -w +$|=1; # outherwise things get mixed up in output + BEGIN { chdir 't' if -d 't'; @INC = qw '../lib ../ext/re'; @@ -24,7 +26,7 @@ use open qw(:utf8 :std); sub fixup_expect { my $expect_ref = shift; - return if $expect_ref eq ""; + return "" if $expect_ref eq ""; my @expect; if (ref $expect_ref) { @@ -82,12 +84,23 @@ sub mark_as_utf8 { my $inf_m1 = ($Config::Config{reg_infty} || 32767) - 1; my $inf_p1 = $inf_m1 + 2; +my $B_hex = sprintf("\\x%02X", ord "B"); +my $low_mixed_alpha = ('A' lt 'a') ? 'A' : 'a'; +my $high_mixed_alpha = ('A' lt 'a') ? 'a' : 'A'; +my $low_mixed_digit = ('A' lt '0') ? 'A' : '0'; +my $high_mixed_digit = ('A' lt '0') ? '0' : 'A'; + +my $colon_hex = sprintf "%02X", ord(":"); +my $tab_hex = sprintf "%02X", ord("\t"); + ## ## Key-value pairs of code/error of code that should have fatal errors. ## my @death = ( '/[[=foo=]]/' => 'POSIX syntax [= =] is reserved for future extensions {#} m/[[=foo=]{#}]/', + '/[[=a]=]]/' => 'POSIX syntax [= =] is reserved for future extensions {#} m/[[=a]=]{#}]/', + '/[[.a].]]/' => 'POSIX syntax [. .] is reserved for future extensions {#} m/[[.a].]{#}]/', '/(?<= .*)/' => 'Variable length lookbehind not implemented in regex m/(?<= .*)/', @@ -131,8 +144,6 @@ my @death = '/(?lil:foo)/' => 'Regexp modifier "l" may not appear twice {#} m/(?lil{#}:foo)/', '/(?aaia:foo)/' => 'Regexp modifier "a" may appear a maximum of twice {#} m/(?aaia{#}:foo)/', '/(?i-l:foo)/' => 'Regexp modifier "l" may not appear after the "-" {#} m/(?i-l{#}:foo)/', -'/a\b{cde/' => 'Use "\b\{" instead of "\b{" {#} m/a\{#}b{cde/', -'/a\B{cde/' => 'Use "\B\{" instead of "\B{" {#} m/a\{#}B{cde/', '/((x)/' => 'Unmatched ( {#} m/({#}(x)/', @@ -176,18 +187,25 @@ my @death = '/[z-a]/' => 'Invalid [] range "z-a" {#} m/[z-a{#}]/', - '/\p/' => 'Empty \p{} {#} m/\p{#}/', - + '/\p/' => 'Empty \p {#} m/\p{#}/', + '/\P/' => 'Empty \P {#} m/\P{#}/', + '/\p{}/' => 'Empty \p{} {#} m/\p{{#}}/', '/\P{}/' => 'Empty \P{} {#} m/\P{{#}}/', - '/(?[[[:word]]])/' => "Unmatched ':' in POSIX class {#} m/(?[[[:word{#}]]])/", - '/(?[[:word]])/' => "Unmatched ':' in POSIX class {#} m/(?[[:word{#}]])/", - '/(?[[[:digit: ])/' => "Unmatched '[' in POSIX class {#} m/(?[[[:digit:{#} ])/", - '/(?[[:digit: ])/' => "Unmatched '[' in POSIX class {#} m/(?[[:digit:{#} ])/", - '/(?[[[::]]])/' => "POSIX class [::] unknown {#} m/(?[[[::]{#}]])/", - '/(?[[[:w:]]])/' => "POSIX class [:w:] unknown {#} m/(?[[[:w:]{#}]])/", - '/(?[[:w:]])/' => "POSIX class [:w:] unknown {#} m/(?[[:w:]{#}])/", + +'/a\b{cde/' => 'Missing right brace on \b{} {#} m/a\b{{#}cde/', +'/a\B{cde/' => 'Missing right brace on \B{} {#} m/a\B{{#}cde/', + + '/\b{}/' => 'Empty \b{} {#} m/\b{}{#}/', + '/\B{}/' => 'Empty \B{} {#} m/\B{}{#}/', + + '/\b{gc}/' => "'gc' is an unknown bound type {#} m/\\b{gc{#}}/", + '/\B{gc}/' => "'gc' is an unknown bound type {#} m/\\B{gc{#}}/", + + '/(?[[[::]]])/' => "Syntax error in (?[...]) in regex m/(?[[[::]]])/", + '/(?[[[:w:]]])/' => "Syntax error in (?[...]) in regex m/(?[[[:w:]]])/", + '/(?[[:w:]])/' => "", + '/[][[:alpha:]]' => "", # [perl #127581] '/(?[a])/' => 'Unexpected character {#} m/(?[a{#}])/', - '/(?[\t])/l' => '(?[...]) not valid in locale {#} m/(?[{#}\t])/', '/(?[ + \t ])/' => 'Unexpected binary operator \'+\' with no preceding operand {#} m/(?[ +{#} \t ])/', '/(?[ \cK - ( + \t ) ])/' => 'Unexpected binary operator \'+\' with no preceding operand {#} m/(?[ \cK - ( +{#} \t ) ])/', '/(?[ \cK ( \t ) ])/' => 'Unexpected \'(\' with no preceding operator {#} m/(?[ \cK ({#} \t ) ])/', @@ -201,8 +219,10 @@ my @death = '/(?[ \x{} ])/' => 'Number with no digits {#} m/(?[ \x{}{#} ])/', '/(?[ \cK + ) ])/' => 'Unexpected \')\' {#} m/(?[ \cK + ){#} ])/', '/(?[ \cK + ])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[ \cK + {#}])/', - '/(?[ \p{foo} ])/' => 'Property \'foo\' is unknown {#} m/(?[ \p{foo}{#} ])/', - '/(?[ \p{ foo = bar } ])/' => 'Property \'foo = bar\' is unknown {#} m/(?[ \p{ foo = bar }{#} ])/', + '/(?[ ( ) ])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[ ( ) {#}])/', + '/(?[[0]+()+])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[[0]+()+{#}])/', + '/(?[ \p{foo} ])/' => 'Can\'t find Unicode property definition "foo" {#} m/(?[ \p{foo}{#} ])/', + '/(?[ \p{ foo = bar } ])/' => 'Can\'t find Unicode property definition "foo = bar" {#} m/(?[ \p{ foo = bar }{#} ])/', '/(?[ \8 ])/' => 'Unrecognized escape \8 in character class {#} m/(?[ \8{#} ])/', '/(?[ \t ]/' => 'Syntax error in (?[...]) in regex m/(?[ \t ]/', '/(?[ [ \t ]/' => 'Syntax error in (?[...]) in regex m/(?[ [ \t ]/', @@ -240,6 +260,9 @@ my @death = 'm/\cß/' => "Character following \"\\c\" must be printable ASCII", '/((?# This is a comment in the middle of a token)?:foo)/' => 'In \'(?...)\', the \'(\' and \'?\' must be adjacent {#} m/((?# This is a comment in the middle of a token)?{#}:foo)/', '/((?# This is a comment in the middle of a token)*FAIL)/' => 'In \'(*VERB...)\', the \'(\' and \'*\' must be adjacent {#} m/((?# This is a comment in the middle of a token)*{#}FAIL)/', + '/(?[\ &!])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[\ &!{#}])/', # [perl #126180] + '/(?[()-!])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[()-!{#}])/', # [perl #126204] + '/(?[!()])/' => 'Incomplete expression within \'(?[ ])\' {#} m/(?[!(){#}])/', # [perl #126404] ); # These are messages that are warnings when not strict; death under 'use re @@ -272,10 +295,10 @@ my @death_only_under_strict = ( => 'Non-hex character {#} m/\x{ABCDEFG{#}}/', 'm/[\x{ABCDEFG}]/' => 'Illegal hexadecimal digit \'G\' ignored', => 'Non-hex character {#} m/[\x{ABCDEFG{#}}]/', - 'm/[[:ascii]]/' => "", - => 'Unmatched \':\' in POSIX class {#} m/[[:ascii{#}]]/', 'm/[\N{}]/' => 'Ignoring zero length \\N{} in character class {#} m/[\\N{}{#}]/', - => 'Zero length \\N{} {#} m/[\\N{}]{#}/', + => 'Zero length \\N{} {#} m/[\\N{}{#}]/', + 'm/\N{}/' => "", + => 'Zero length \\N{} {#} m/\\N{}{#}/', "m'[\\y]\\x{100}'" => 'Unrecognized escape \y in character class passed through {#} m/[\y{#}]\x{100}/', => 'Unrecognized escape \y in character class {#} m/[\y{#}]\x{100}/', 'm/[a-\d]\x{100}/' => 'False [] range "a-\d" {#} m/[a-\d{#}]\x{100}/', @@ -375,7 +398,7 @@ my @death_utf8 = mark_as_utf8( '/ネ[\x{ネ]/' => 'Missing right brace on \x{} {#} m/ネ[\x{{#}ネ]/', '/ネ\o{ネ/' => 'Missing right brace on \o{ {#} m/ネ\o{{#}ネ/', - '/ネ[[:ネ:]]ネ/' => 'POSIX class [:ネ:] unknown {#} m/ネ[[:ネ:]{#}]ネ/', + '/ネ[[:ネ:]]ネ/' => "", '/ネ[[=ネ=]]ネ/' => 'POSIX syntax [= =] is reserved for future extensions {#} m/ネ[[=ネ=]{#}]ネ/', @@ -385,13 +408,12 @@ my @death_utf8 = mark_as_utf8( '/ネ\p{}ネ/' => 'Empty \p{} {#} m/ネ\p{{#}}ネ/', - '/ネ(?[[[:ネ]]])ネ/' => "Unmatched ':' in POSIX class {#} m/ネ(?[[[:ネ{#}]]])ネ/", - '/ネ(?[[[:ネ: ])ネ/' => "Unmatched '[' in POSIX class {#} m/ネ(?[[[:ネ:{#} ])ネ/", - '/ネ(?[[[::]]])ネ/' => "POSIX class [::] unknown {#} m/ネ(?[[[::]{#}]])ネ/", - '/ネ(?[[[:ネ:]]])ネ/' => "POSIX class [:ネ:] unknown {#} m/ネ(?[[[:ネ:]{#}]])ネ/", - '/ネ(?[[:ネ:]])ネ/' => "POSIX class [:ネ:] unknown {#} m/ネ(?[[:ネ:]{#}])ネ/", + '/ネ(?[[[:ネ]]])ネ/' => "Syntax error in (?[...]) in regex m/ネ(?[[[:ネ]]])ネ/", + '/ネ(?[[[:ネ: ])ネ/' => "Syntax error in (?[...]) in regex m/ネ(?[[[:ネ: ])ネ/", + '/ネ(?[[[::]]])ネ/' => "Syntax error in (?[...]) in regex m/ネ(?[[[::]]])ネ/", + '/ネ(?[[[:ネ:]]])ネ/' => "Syntax error in (?[...]) in regex m/ネ(?[[[:ネ:]]])ネ/", + '/ネ(?[[:ネ:]])ネ/' => "", '/ネ(?[ネ])ネ/' => 'Unexpected character {#} m/ネ(?[ネ{#}])ネ/', - '/ネ(?[ネ])/l' => '(?[...]) not valid in locale {#} m/ネ(?[{#}ネ])/', '/ネ(?[ + [ネ] ])/' => 'Unexpected binary operator \'+\' with no preceding operand {#} m/ネ(?[ +{#} [ネ] ])/', '/ネ(?[ \cK - ( + [ネ] ) ])/' => 'Unexpected binary operator \'+\' with no preceding operand {#} m/ネ(?[ \cK - ( +{#} [ネ] ) ])/', '/ネ(?[ \cK ( [ネ] ) ])/' => 'Unexpected \'(\' with no preceding operator {#} m/ネ(?[ \cK ({#} [ネ] ) ])/', @@ -400,12 +422,14 @@ my @death_utf8 = mark_as_utf8( '/(?[ \o{ネ} ])ネ/' => 'Non-octal character {#} m/(?[ \o{ネ{#}} ])ネ/', '/ネ(?[ \o{} ])ネ/' => 'Number with no digits {#} m/ネ(?[ \o{}{#} ])ネ/', '/(?[ \x{ネ} ])ネ/' => 'Non-hex character {#} m/(?[ \x{ネ{#}} ])ネ/', - '/(?[ \p{ネ} ])/' => 'Property \'ネ\' is unknown {#} m/(?[ \p{ネ}{#} ])/', - '/(?[ \p{ ネ = bar } ])/' => 'Property \'ネ = bar\' is unknown {#} m/(?[ \p{ ネ = bar }{#} ])/', + '/(?[ \p{ネ} ])/' => 'Can\'t find Unicode property definition "ネ" {#} m/(?[ \p{ネ}{#} ])/', + '/(?[ \p{ ネ = bar } ])/' => 'Can\'t find Unicode property definition "ネ = bar" {#} m/(?[ \p{ ネ = bar }{#} ])/', '/ネ(?[ \t ]/' => 'Syntax error in (?[...]) in regex m/ネ(?[ \t ]/', '/(?[ \t + \e # ネ This was supposed to be a comment ])/' => 'Syntax error in (?[...]) in regex m/(?[ \t + \e # ネ This was supposed to be a comment ])/', 'm/(*ネ)ネ/' => q, '/\cネ/' => "Character following \"\\c\" must be printable ASCII", + '/\b{ネ}/' => "'ネ' is an unknown bound type {#} m/\\b{ネ{#}}/", + '/\B{ネ}/' => "'ネ' is an unknown bound type {#} m/\\B{ネ{#}}/", ); push @death, @death_utf8; @@ -439,7 +463,12 @@ my @death_utf8_only_under_strict = ( my @warning = ( 'm/\b*\x{100}/' => '\b* matches null string many times {#} m/\b*{#}\x{100}/', + '/\b{g}/a' => "Using /u for '\\b{g}' instead of /a {#} m/\\b{g}{#}/", + '/\B{gcb}/a' => "Using /u for '\\B{gcb}' instead of /a {#} m/\\B{gcb}{#}/", 'm/[:blank:]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes {#} m/[:blank:]{#}\x{100}/', + 'm/[[:cntrl:]][:^ascii:]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes {#} m/[[:cntrl:]][:^ascii:]{#}\x{100}/', + 'm/[[:ascii]]\x{100}/' => "Assuming NOT a POSIX class since there is no terminating ':' {#} m/[[:ascii{#}]]\\x{100}/", + 'm/(?[[:word]])\x{100}/' => "Assuming NOT a POSIX class since there is no terminating ':' {#} m/(?[[:word{#}]])\\x{100}/", "m'\\y\\x{100}'" => 'Unrecognized escape \y passed through {#} m/\y{#}\x{100}/', '/x{3,1}/' => 'Quantifier {n,m} with n > m can\'t match {#} m/x{3,1}{#}/', '/\08/' => '\'\08\' resolved to \'\o{0}8\' {#} m/\08{#}/', @@ -458,8 +487,10 @@ my @warning = ( '/\_/' => "", '/[\006]/' => "", '/[:alpha:]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes {#} m/[:alpha:]{#}\x{100}/', - '/[:zog:]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes {#} m/[:zog:]{#}\x{100}/', - '/[.zog.]\x{100}/' => 'POSIX syntax [. .] belongs inside character classes {#} m/[.zog.]{#}\x{100}/', + '/[:zog:]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes (but this one isn\'t fully valid) {#} m/[:zog:]{#}\x{100}/', + '/[.zog.]\x{100}/' => 'POSIX syntax [. .] belongs inside character classes (but this one isn\'t implemented) {#} m/[.zog.]{#}\x{100}/', + '/[.z#g.]\x{100}/x' => "", # Runs into a comment + '/[.z\#g.]\x{100}/x' => 'POSIX syntax [. .] belongs inside character classes (but this one isn\'t implemented) {#} m/[.z\#g.]{#}\x{100}/', '/[a-b]/' => "", '/(?c)\x{100}/' => 'Useless (?c) - use /gc modifier {#} m/(?c{#})\x{100}/', '/(?-c)\x{100}/' => 'Useless (?-c) - don\'t use /gc modifier {#} m/(?-c{#})\x{100}/', @@ -482,14 +513,60 @@ my @warning = ( 'Useless (?c) - use /gc modifier {#} m/(?ogc{#})\x{100}/', ], '/a{1,1}?\x{100}/' => 'Useless use of greediness modifier \'?\' {#} m/a{1,1}?{#}\x{100}/', - '/b{3} +\x{100}/x' => 'Useless use of greediness modifier \'+\' {#} m/b{3} +{#}\x{100}/', + "/(?[ [ % - % ] ])/" => "", + "/(?[ [ : - \\x$colon_hex ] ])\\x{100}/" => "\": - \\x$colon_hex \" is more clearly written simply as \":\" {#} m/(?[ [ : - \\x$colon_hex {#}] ])\\x{100}/", + "/(?[ [ \\x$colon_hex - : ] ])\\x{100}/" => "\"\\x$colon_hex\ - : \" is more clearly written simply as \":\" {#} m/(?[ [ \\x$colon_hex - : {#}] ])\\x{100}/", + "/(?[ [ \\t - \\x$tab_hex ] ])\\x{100}/" => "\"\\t - \\x$tab_hex \" is more clearly written simply as \"\\t\" {#} m/(?[ [ \\t - \\x$tab_hex {#}] ])\\x{100}/", + "/(?[ [ \\x$tab_hex - \\t ] ])\\x{100}/" => "\"\\x$tab_hex\ - \\t \" is more clearly written simply as \"\\t\" {#} m/(?[ [ \\x$tab_hex - \\t {#}] ])\\x{100}/", + "/(?[ [ $B_hex - C ] ])/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/(?[ [ $B_hex - C {#}] ])/", + "/(?[ [ A - $B_hex ] ])/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/(?[ [ A - $B_hex {#}] ])/", + "/(?[ [ $low_mixed_alpha - $high_mixed_alpha ] ])/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/(?[ [ $low_mixed_alpha - $high_mixed_alpha {#}] ])/", + "/(?[ [ $low_mixed_digit - $high_mixed_digit ] ])/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/(?[ [ $low_mixed_digit - $high_mixed_digit {#}] ])/", + "/[alnum]/" => "", + "/[^alnum]/" => "", + '/[:blank]\x{100}/' => 'POSIX syntax [: :] belongs inside character classes (but this one isn\'t fully valid) {#} m/[:blank{#}]\x{100}/', + '/[[:digit]]\x{100}/' => 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[:digit{#}]]\x{100}/', # [perl # 8904] + '/[[:digit:foo]\x{100}/' => 'Assuming NOT a POSIX class since there is no terminating \']\' {#} m/[[:digit:{#}foo]\x{100}/', + '/[[:di#it:foo]\x{100}/x' => 'Assuming NOT a POSIX class since there is no terminating \']\' {#} m/[[:di#it:{#}foo]\x{100}/', + '/[[:dgit]]\x{100}/' => 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[:dgit{#}]]\x{100}/', + '/[[:dgit:foo]\x{100}/' => 'Assuming NOT a POSIX class since there is no terminating \']\' {#} m/[[:dgit:{#}foo]\x{100}/', + '/[[:dgt]]\x{100}/' => "", # Far enough away from a real class to not be recognized as one + '/[[:dgt:foo]\x{100}/' => "", + '/[[:DIGIT]]\x{100}/' => [ 'Assuming NOT a POSIX class since the name must be all lowercase letters {#} m/[[:DIGIT{#}]]\x{100}/', + 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[:DIGIT{#}]]\x{100}/', + ], + '/[[digit]\x{100}/' => [ 'Assuming NOT a POSIX class since there must be a starting \':\' {#} m/[[{#}digit]\x{100}/', + 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[digit{#}]\x{100}/', + ], + '/[[alpha]]\x{100}/' => [ 'Assuming NOT a POSIX class since there must be a starting \':\' {#} m/[[{#}alpha]]\x{100}/', + 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[alpha{#}]]\x{100}/', + ], + '/[[^word]\x{100}/' => [ 'Assuming NOT a POSIX class since the \'^\' must come after the colon {#} m/[[^{#}word]\x{100}/', + 'Assuming NOT a POSIX class since there must be a starting \':\' {#} m/[[^{#}word]\x{100}/', + 'Assuming NOT a POSIX class since there is no terminating \':\' {#} m/[[^word{#}]\x{100}/', + ], + '/[[ ^ : x d i g i t : ] ]\x{100}/' => [ 'Assuming NOT a POSIX class since no blanks are allowed in one {#} m/[[ {#}^ : x d i g i t : ] ]\x{100}/', + 'Assuming NOT a POSIX class since the \'^\' must come after the colon {#} m/[[ ^{#} : x d i g i t : ] ]\x{100}/', + 'Assuming NOT a POSIX class since no blanks are allowed in one {#} m/[[ ^ {#}: x d i g i t : ] ]\x{100}/', + 'Assuming NOT a POSIX class since no blanks are allowed in one {#} m/[[ ^ : {#}x d i g i t : ] ]\x{100}/', + 'Assuming NOT a POSIX class since no blanks are allowed in one {#} m/[[ ^ : x d i g i t : ]{#} ]\x{100}/', + ], + '/[foo:lower:]]\x{100}/' => 'Assuming NOT a POSIX class since it doesn\'t start with a \'[\' {#} m/[foo{#}:lower:]]\x{100}/', + '/[[;upper;]]\x{100}/' => [ 'Assuming NOT a POSIX class since a semi-colon was found instead of a colon {#} m/[[;{#}upper;]]\x{100}/', + 'Assuming NOT a POSIX class since a semi-colon was found instead of a colon {#} m/[[;upper;]{#}]\x{100}/', + ], + '/[foo;punct;]]\x{100}/' => [ 'Assuming NOT a POSIX class since it doesn\'t start with a \'[\' {#} m/[foo{#};punct;]]\x{100}/', + 'Assuming NOT a POSIX class since a semi-colon was found instead of a colon {#} m/[foo;{#}punct;]]\x{100}/', + 'Assuming NOT a POSIX class since a semi-colon was found instead of a colon {#} m/[foo;punct;]{#}]\x{100}/', + ], + ); # See comments before this for why '\x{100}' is generally needed # These need the character 'ネ' as a marker for mark_as_utf8() my @warnings_utf8 = mark_as_utf8( 'm/ネ\b*ネ/' => '\b* matches null string many times {#} m/ネ\b*{#}ネ/', '/(?=ネ)*/' => '(?=ネ)* matches null string many times {#} m/(?=ネ)*{#}/', - 'm/ネ[:foo:]ネ/' => 'POSIX syntax [: :] belongs inside character classes {#} m/ネ[:foo:]{#}ネ/', + 'm/ネ[:foo:]ネ/' => 'POSIX syntax [: :] belongs inside character classes (but this one isn\'t fully valid) {#} m/ネ[:foo:]{#}ネ/', '/ネ(?c)ネ/' => 'Useless (?c) - use /gc modifier {#} m/ネ(?c{#})ネ/', '/utf8 ネ (?ogc) ネ/' => [ 'Useless (?o) - use /o modifier {#} m/utf8 ネ (?o{#}gc) ネ/', @@ -501,6 +578,39 @@ my @warnings_utf8 = mark_as_utf8( push @warning, @warnings_utf8; +my @warning_only_under_strict = ( + '/[\N{U+00}-\x01]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\N{U+00}-\x01{#}]\x{100}/', + '/[\x00-\N{SOH}]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\x00-\N{U+01}{#}]\x{100}/', + '/[\N{DEL}-\o{377}]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\N{U+7F}-\o{377}{#}]\x{100}/', + '/[\o{0}-\N{U+01}]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\o{0}-\N{U+01}{#}]\x{100}/', + '/[\000-\N{U+01}]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\000-\N{U+01}{#}]\x{100}/', + '/[\N{DEL}-\377]\x{100}/' => 'Both or neither range ends should be Unicode {#} m/[\N{U+7F}-\377{#}]\x{100}/', + '/[\N{U+00}-A]\x{100}/' => 'Ranges of ASCII printables should be some subset of "0-9", "A-Z", or "a-z" {#} m/[\N{U+00}-A{#}]\x{100}/', + '/[a-\N{U+FF}]\x{100}/' => 'Ranges of ASCII printables should be some subset of "0-9", "A-Z", or "a-z" {#} m/[a-\N{U+FF}{#}]\x{100}/', + '/[\N{U+00}-\a]\x{100}/' => "", + '/[\a-\N{U+FF}]\x{100}/' => "", + '/[\N{U+FF}-\x{100}]/' => 'Both or neither range ends should be Unicode {#} m/[\N{U+FF}-\x{100}{#}]/', + '/[\N{U+100}-\x{101}]/' => "", + "/[%-%]/" => "", + "/[:-\\x$colon_hex]\\x{100}/" => "\":-\\x$colon_hex\" is more clearly written simply as \":\" {#} m/[:-\\x$colon_hex\{#}]\\x{100}/", + "/[\\x$colon_hex-:]\\x{100}/" => "\"\\x$colon_hex-:\" is more clearly written simply as \":\" {#} m/[\\x$colon_hex\-:{#}]\\x{100}/", + "/[\\t-\\x$tab_hex]\\x{100}/" => "\"\\t-\\x$tab_hex\" is more clearly written simply as \"\\t\" {#} m/[\\t-\\x$tab_hex\{#}]\\x{100}/", + "/[\\x$tab_hex-\\t]\\x{100}/" => "\"\\x$tab_hex-\\t\" is more clearly written simply as \"\\t\" {#} m/[\\x$tab_hex\-\\t{#}]\\x{100}/", + "/[$B_hex-C]/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/[$B_hex-C{#}]/", + "/[A-$B_hex]/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/[A-$B_hex\{#}]/", + "/[$low_mixed_alpha-$high_mixed_alpha]/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/[$low_mixed_alpha-$high_mixed_alpha\{#}]/", + "/[$low_mixed_digit-$high_mixed_digit]/" => "Ranges of ASCII printables should be some subset of \"0-9\", \"A-Z\", or \"a-z\" {#} m/[$low_mixed_digit-$high_mixed_digit\{#}]/", +); + +my @warning_utf8_only_under_strict = mark_as_utf8( + '/ネ[᪉-᪐]/; #no latin1' => "Ranges of digits should be from the same group of 10 {#} m/ネ[᪉-᪐{#}]/", + '/ネ(?[ [ ᪉ - ᪐ ] ])/; #no latin1' => "Ranges of digits should be from the same group of 10 {#} m/ネ(?[ [ ᪉ - ᪐ {#}] ])/", + '/ネ[᧙-᧚]/; #no latin1' => "Ranges of digits should be from the same group of 10 {#} m/ネ[᧙-᧚{#}]/", + '/ネ(?[ [ ᧙ - ᧚ ] ])/; #no latin1' => "Ranges of digits should be from the same group of 10 {#} m/ネ(?[ [ ᧙ - ᧚ {#}] ])/", +); + +push @warning_only_under_strict, @warning_utf8_only_under_strict; + my @experimental_regex_sets = ( '/(?[ \t ])/' => 'The regex_sets feature is experimental {#} m/(?[{#} \t ])/', 'use utf8; /utf8 ネ (?[ [\tネ] ])/' => do { use utf8; 'The regex_sets feature is experimental {#} m/utf8 ネ (?[{#} [\tネ] ])/' }, @@ -537,104 +647,131 @@ for my $strict ("", "use re 'strict';") { $death_utf8_only_under_strict[$i+2]); } } -for (my $i = 0; $i < @death; $i += 2) { - my $regex = $death[$i]; - my $expect = fixup_expect($death[$i+1]); - no warnings 'experimental::regex_sets'; - no warnings 'experimental::re_strict'; - # skip the utf8 test on EBCDIC since they do not die - next if $::IS_EBCDIC && $regex =~ /utf8/; - - warning_is(sub { - my $eval_string = "$strict $regex"; - $_ = "x"; - eval $eval_string; - like($@, qr/\Q$expect/, $eval_string); - }, undef, "... and died without any other warnings"); -} + for (my $i = 0; $i < @death; $i += 2) { + my $regex = $death[$i]; + my $expect = fixup_expect($death[$i+1]); + no warnings 'experimental::regex_sets'; + no warnings 'experimental::re_strict'; + + warning_is(sub { + my $eval_string = "$strict $regex"; + $_ = "x"; + eval $eval_string; + like($@, qr/\Q$expect/, $eval_string); + }, undef, "... and died without any other warnings"); + } } -for my $strict ("no warnings 'experimental::re_strict'; use re 'strict';", "") { +for my $strict ("", "no warnings 'experimental::re_strict'; use re 'strict';") { + my @warning_tests = @warning; - # First time through we use strict to make sure that that doesn't change - # any of the warnings into fatal, and outputs them correctly. The second - # time we don't use strict, and add the messages that are warnings when - # not under strict to the list of warnings. This checks that non-strict - # works. - if (! $strict) { + # Build the tests for @warning. Use the strict/non-strict versions + # appropriately. + if ($strict) { + push @warning_tests, @warning_only_under_strict; + } + else { + for (my $i = 0; $i < @warning_only_under_strict; $i += 2) { + if ($warning_only_under_strict[$i] =~ /\Q(?[/) { + push @warning_tests, $warning_only_under_strict[$i], # The regex + $warning_only_under_strict[$i+1]; + } + else { + push @warning_tests, $warning_only_under_strict[$i], # The regex + ""; # No warning because not strict + } + } for (my $i = 0; $i < @death_only_under_strict; $i += 3) { - push @warning, $death_only_under_strict[$i], # The regex - $death_only_under_strict[$i+1]; # The warning + push @warning_tests, $death_only_under_strict[$i], # The regex + $death_only_under_strict[$i+1]; # The warning } for (my $i = 0; $i < @death_utf8_only_under_strict; $i += 3) { - push @warning, mark_as_utf8($death_utf8_only_under_strict[$i], $death_utf8_only_under_strict[$i+1]); + push @warning_tests, mark_as_utf8($death_utf8_only_under_strict[$i], + $death_utf8_only_under_strict[$i+1]); } } -foreach my $ref (\@warning, \@experimental_regex_sets, \@deprecated) { - my $warning_type; - my $default_on; - if ($ref == \@warning) { - $warning_type = 'regexp, digit'; - $default_on = $strict; - } - elsif ($ref == \@deprecated) { - $warning_type = 'regexp, deprecated'; - $default_on = 1; - } - else { - $warning_type = 'experimental::regex_sets'; - $default_on = 1; - } - for (my $i = 0; $i < @$ref; $i += 2) { - my $regex = $ref->[$i]; - my @expect = fixup_expect($ref->[$i+1]); - { - $_ = "x"; - eval "$strict no warnings; $regex"; + + foreach my $ref (\@warning_tests, \@experimental_regex_sets, \@deprecated) { + my $warning_type; + my $turn_off_warnings = ""; + my $default_on; + if ($ref == \@warning_tests) { + $warning_type = 'regexp, digit'; + $turn_off_warnings = "no warnings 'experimental::regex_sets';"; + $default_on = $strict; } - if (is($@, "", "$strict $regex did not die")) { - my @got = capture_warnings(sub { - $_ = "x"; - eval "$strict $regex" }); - my $count = @expect; - if (! is(scalar @got, scalar @expect, "... and gave expected number ($count) of warnings")) { - if (@got < @expect) { - $count = @got; - note "Expected warnings not gotten:\n\t" . join "\n\t", @expect[$count .. $#expect]; - } - else { - note "Unexpected warnings gotten:\n\t" . join("\n\t", @got[$count .. $#got]); - } + elsif ($ref == \@deprecated) { + $warning_type = 'regexp, deprecated'; + $default_on = 1; + } + else { + $warning_type = 'experimental::regex_sets'; + $default_on = 1; + } + for (my $i = 0; $i < @$ref; $i += 2) { + my $regex = $ref->[$i]; + my @expect = fixup_expect($ref->[$i+1]); + + # A length-1 array with an empty warning means no warning gets + # generated at all. + undef @expect if @expect == 1 && $expect[0] eq ""; + + { + $_ = "x"; + eval "$strict no warnings; $regex"; } - foreach my $i (0 .. $count - 1) { - if (! like($got[$i], qr/\Q$expect[$i]/, "... and gave expected warning")) { - chomp($got[$i]); - chomp($expect[$i]); - diag("GOT\n'$got[$i]'\nEXPECT\n'$expect[$i]'"); + if (is($@, "", "$strict $regex did not die")) { + my @got = capture_warnings(sub { + $_ = "x"; + eval "$strict $turn_off_warnings $regex" }); + my $count = @expect; + if (! is(scalar @got, scalar @expect, + "... and gave expected number ($count) of warnings")) + { + if (@got < @expect) { + $count = @got; + note "Expected warnings not gotten:\n\t" . join "\n\t", + @expect[$count .. $#expect]; + } + else { + note "Unexpected warnings gotten:\n\t" . join("\n\t", + @got[$count .. $#got]); + } } - else { - ok (0 == capture_warnings(sub { - $_ = "x"; - eval "$strict no warnings '$warning_type'; $regex;" } - ), - "... and turning off '$warning_type' warnings suppressed it"); - - # Test that whether the warning is on by default is - # correct. This test relies on the fact that we - # are outside the scope of any ‘use warnings’. - local $^W; - my @warns = capture_warnings(sub { $_ = "x"; eval "$strict $regex" }); - if ($default_on) { - ok @warns > 0, "... and the warning is on by default"; + foreach my $i (0 .. $count - 1) { + if (! like($got[$i], qr/\Q$expect[$i]/, + "... and gave expected warning")) + { + chomp($got[$i]); + chomp($expect[$i]); + diag("GOT\n'$got[$i]'\nEXPECT\n'$expect[$i]'"); } else { - ok @warns == 0, "... and the warning is off by default"; + ok (0 == capture_warnings(sub { + $_ = "x"; + eval "$strict no warnings '$warning_type'; $regex;" } + ), + "... and turning off '$warning_type' warnings suppressed it"); + + # Test that whether the warning is on by default is + # correct. This test relies on the fact that we + # are outside the scope of any ‘use warnings’. + local $^W; + my @warns = capture_warnings(sub { $_ = "x"; + eval "$strict $regex" }); + # Warning should be on as well if is testing + # '(?[...])' which turns on strict + if ($default_on || grep { $_ =~ /\Q(?[/ } @expect ) { + ok @warns > 0, "... and the warning is on by default"; + } + else { + ok @warns == 0, "... and the warning is off by default"; + } } } } } } } -} done_testing();