Revision history for Perl500503Syntax::OrDie
0.04 2026-09-21 JST (Japan Standard Time)
- The \x{HHHH} and \N{name} escapes are now detected inside
interpolating string literals, which is where they are actually
written. Both were listed in CHECKED CONSTRUCTS and both were, in
practice, unreachable: the bodies of string literals are masked
before the blacklist runs, so the entries could fire only on the
rare source that puts the escape outside a string, and the regex
stage covered it only inside a regex. The ordinary spelling
my $smiley = "\x{263A}";
passed unreported -- the exact defect this distribution exists to
catch, since Perl 5.005_03 reads \x{263A} as \x with no hex digits
followed by the literal text "{263A}" and carries on with the wrong
string.
A new stage 4 scans the body of every interpolating string literal:
"...", qq//, qx//, `...`, an unquoted or double-quoted heredoc, and
the replacement half of s/// unless it is single-quote delimited.
'...', q//, qw// and <<'WORD' process no escapes and are not
scanned. Escaped backslashes are neutralised first, so "\\x{41}"
-- a literal backslash followed by text -- is not reported.
- doc/: all 21 cheatsheets still stated version 0.03 while everything
else in this release had moved on, and the suite reported success
throughout because nothing read that number. They now state the
current version, and t/9080-cheatsheets.t gained CS12, which
compares the version each sheet states against $VERSION -- the doc/
counterpart of the B8 check INA_CPAN_Check makes on =head1 VERSION.
- t/0002-check-source.t: the xUNI case asserted the old behaviour
("inside dquote string - masked (not detected)"). It now asserts
detection, and six cases were added for the single-quoted,
escaped-backslash, heredoc and s/// spellings.
- POD and README: the CHECKED CONSTRUCTS entries for \x{} and \N{}
now say where the escapes are looked for, and the "RAW checks"
section, which claimed that no string content is ever inspected,
has been split so that the rule it states is the one the code
follows: what a string says is not inspected, how it is spelled is.
- Three constructs that a port to Perl 5.005_03 meets early were
checked by nothing, and all three fail on 5.005_03 for reasons that
have nothing to do with what a string in them says:
* 'use warnings' and 'no warnings' without the
$INC{'warnings.pm'} stub. The pragma is Perl 5.6 and the bare
statement dies at compile time with "Can't locate warnings.pm".
The guarded idiom that every cross-version file in this author's
distributions already carries is a no-op on 5.005_03 and is
treated as compatible, the same way the empty-import form of
'use feature' is. The stub counts only where it is live code,
so the idiom quoted in a comment or in POD does not excuse an
unguarded statement.
* a lexical filehandle or directory handle -- open(my $fh, ...),
opendir, sysopen, pipe, socket, socketpair, accept. Perl
5.005_03 has nothing to autovivify a handle into and fails at
run time with "Can't use an undefined value as a symbol
reference", so the defect survives compilation. Only the 'my'
spelling is reported: open($fh, ...) against a lexical that
already holds a glob is valid 5.005_03 and cannot be told apart
from the invalid case.
* binmode() with a LAYER argument. This is a question of arity,
not of the layer string: 5.005_03 binmode takes the filehandle
alone and rejects a second argument with "Too many arguments for
binmode". The earlier decision to drop the PerlIO-layer entry
from RAW_BLACKLIST reasoned about the string, which is why the
call was left unchecked; the commas are counted at the top level
of the call instead, as the 3-argument open() stage already
does.
Verified against the 23 corpus modules and this distribution's own
sources, which carry guarded 'use warnings' and one-argument
binmode() throughout: no false positives. t/0002-check-source.t
gained 25 cases, and t/0005-regex-checks.t had two that asserted
the old reasoning ("binmode with layer string - NOT a syntax
violation"); they now assert detection, with two more for the layer
held in a variable and for the layer string on its own. doc/ and
README list the three constructs.
- t/corpus-stack: Jacode4e, Jacode4e::RoundTrip and mb shipped as
complete copies of their distributions -- 3.4 MB of the 5.2 MB this
distribution weighed, against 64 KB of lib/. Almost none of it
reached the scanner. Measured with the masker itself, 99% of
Jacode4e.pm and of RoundTrip.pm, and 73% of mb.pm, was POD, data
tables and text after __END__: regions the masker blanks before any
stage runs. The three now ship as excerpts and the distribution is
1.2 MB.
Only lines the masker renders as dead were removed. Lines inside a
regex literal or an interpolating string literal were kept, because
stages 3 and 4 read those, and so were POD directives, heredoc
terminators, __END__ and three lines of context on each side of
every removed run, so that each file still parses and still reads
as the source it came from. No marker was written where lines were
removed: a marker is a line of text dropped into whatever quoted
region surrounds it, and one delimiter character in it -- a paren
inside qw(...) -- would close that region early and change what the
scanner sees. Each file carries one header block instead.
The reduction was verified against the full sources: the masked
live code, the list of regex bodies and the list of string bodies
are byte-identical before and after, and all three still report
zero violations.
- t/0011-corpus-excerpt.t (new): guards the properties that can be
checked from inside the distribution -- a 256 KB cap on every
corpus module, the excerpt header, and the agreement between the
module named in that header and the package the file declares.
The cap is the one that matters: a corpus is refreshed by copying a
newer release over the old file, and the obvious way to do that is
to copy the whole thing.
- META.yml now conforms to the Meta 1.4 specification. The
meta-spec url was written as an https URL, and CPAN::Meta::Validator
compares that value literally against the one the specification
names, so the file was rejected with "Unknown META specification"
and the CPANTS Kwalitee indicator
meta_yml_conforms_to_known_spec failed. The url is now the
http form the specification itself uses.
- Rebuilt the distribution metadata with pmake.bat 0.51, which
writes the corrected url and declares the test-phase
prerequisites separately from the runtime ones.
- Updated the bundled t/lib/INA_CPAN_Check.pm to 0.44. Its ok()
now carries an ($;$) prototype, which forces a match handed to it
into scalar context; without it an assertion written as
ok($out =~ /.../, 'name') silently passed whenever the match
failed.
- t/0007-selfcheck.t: run_cmd() built a fully quoted command line and
opened it as a pipe. On Windows that line reaches cmd.exe, which
strips the first and the last quote character of the line it is
given, so a line beginning with the quoted absolute path in $^X was
torn in half: the child never ran and run_cmd() returned the empty
string for every call. The whole file still reported success there
only because of the list-context defect above. It now runs the
child with system() and a LIST, which uses no shell on either
platform, and captures the output through redirected handles.
- t/0007-selfcheck.t: SC05 asserted that the module reports a
violation when scanned against itself, describing a qr//
false positive as a documented limitation. The module is in
fact clean against itself, and the assertion only appeared to
pass because of the list-context defect above. It now asserts
that the self-check finds no violations.
0.03 2026-05-31 JST (Japan Standard Time)
- Updated the README DOCUMENTATION section to list the actual 21
cheatsheet languages shipped in doc/ (it still named the eleven
removed languages and omitted the eleven new ones).
- Audited every cheatsheet against the module implementation and
corrected the abbreviated "CHECKED CONSTRUCTS (static)" block used by
the template-language cheatsheets, which was both wrong and stale:
* removed "PerlIO layers" from the checked list -- PerlIO layers
are runtime string values and are deliberately NOT checked (the
perl58delta note now says so explicitly);
* the Perl 5.8 entry now lists the "use constant { HASH }"
multi-constant form that the module actually detects;
* added the constructs that were missing entirely: *name{SLOT}
(5.6), my sub / state sub (5.18), key/value slices (5.20),
string bitwise operators and foreach reference aliasing (5.22),
variable-length lookbehind (5.30), ^^ / ^^= and __CLASS__ (5.40),
and any/all BLOCK LIST, my method, ->&name (5.42).
The English and Japanese full cheatsheets already matched the
implementation and were left as-is apart from the version bump.
- Regenerated the doc/ cheatsheets as the standard 21-language set
(BM BN EN FR HI ID JA KM KO MN MY NE SI TH TL TR TW UR UZ VI ZH).
Eleven non-standard languages (AR CS DE ES IT NL PL PT RU SV UK)
had been introduced by mistake and are removed; MANIFEST now matches.
- Fixed UTF-8 double-encoding (mojibake) in twelve cheatsheets and a
literal "EOF_UNDERLINE" placeholder left on the title-underline line
of fourteen cheatsheets. All cheatsheets are now well-formed UTF-8.
- Synchronised the version in README and in every cheatsheet to 0.03
(they previously read 0.01 while the rest of the distribution was
already 0.03).
- Fixed eg/check_compatibility.pl: it wrapped check_source() in
eval{}/if($@), but check_source() returns a violation list rather
than dying, so every example printed "(not detected)". The examples
now use the return-value API and report violations correctly.
- Canonicalised the module header: added the distribution URL and a
"use 5.00503;" minimum-version declaration.
- Fixed a false positive in t/9020-perl5compat.t (P5): the
"use VERSION >= 5.6" pattern stripped the leading zeros of 5.00503
and flagged the legitimate minimum-version line. P5 now mirrors the
module's own @BLACKLIST alternation.
- Closed test blind spots:
* t/9080-cheatsheets.t lists the standard 21 languages, adds a
per-language guard against UTF-8 double-encoding, and declares
its plan via plan_tests() so a failure also exits non-zero.
* t/9070-examples.t now runs the demonstration script and asserts
it reports violations (guarding the eval/$@ regression).
* t/9060-readme.t H9 genuinely gates on the module version. The
list-context idiom "ok($x =~ /.../, name)" used by the README
checks let a failed match shift the test name into the truth
slot and pass vacuously; the predicates are now scalar-forced.
* t/9030-distribution.t language list updated to the standard set.
- Fixed t/9050-pod.t failing on every Perl from 5.16 through 5.24.
The test called Pod::Checker->parse_file(), a method that exists only
in the Pod::Simple-based Pod::Checker (1.73+, shipped with Perl 5.26
and later). The Pod::Parser-based versions bundled with those Perls
(1.51 with 5.16; 1.60 with 5.18-5.24) provide only parse_from_file(),
so parse_file() threw, the eval trapped it, and subtest G12 (errors)
failed regardless of the actual POD content. The test now selects the
available API via ->can('parse_file'); the Pod::Parser path routes
messages to STDERR so the TAP stream is never corrupted.
- Fixed three "unescaped <>" POD warnings reported by Pod::Checker 1.60
(Perl 5.18-5.24), which would have failed subtest G13 once G12 ran:
escaped the bare '>' in "VERSION >= 5.6" / "VERSION >= v5.6" as
E<gt>=, and wrapped the match-position variables @-, $-[N] in C<>
so no bare '>' remains in the CHECKED CONSTRUCTS list.
- Fixed false positive: an empty pattern // used as the first argument
of split/grep/map, e.g. "for my $ch (split //, $str)", is no longer
reported as the Perl 5.10 defined-or operator. The source masker now
recognizes split/grep/map as regex-introducing list operators, so a
'/' immediately following one of these barewords is masked as the
start of a pattern (a division can never follow these operators).
- Added regression tests for the fix in t/0008-new-features.t.
- Fixed false positive: a subroutine definition "sub any { ... }" or
"sub all { ... }" is no longer reported as the Perl 5.42 "any BLOCK
LIST" / "all BLOCK LIST" keyword operator. Matches preceded by "sub"
or by an arrow operator are now skipped.
- Fixed false positive: a 2-argument open() whose second argument
contains a function call with a comma, e.g.
open(FH, '>' . File::Spec->catfile($dir, $file)), is no longer
mistaken for a 3-argument open(). Detection now counts only
top-level commas inside the open(...) parentheses (paren-aware).
- Added regression tests for both fixes in t/0008-new-features.t.
- Fixed false positive: here-document bodies are now masked correctly.
The source masker previously consumed only the sentinel-length of a
heredoc body and could not handle two or more heredocs sharing one
line (e.g. "$_ = <<'A'; $x eq <<'B';"). Heredoc bodies that contained
post-5.005_03 example text (<<>>, $+[N], tr///r, subroutine
signatures, //x, ...) leaked into the code scan and were wrongly
reported. The masker now queues each heredoc sentinel at its <<
operator and consumes all queued bodies, in order, at the end of the
line.
- Fixed false positive: a typeglob ("local (*s, $n) = @_;") or a
subroutine sigil ("&s") immediately before a quote-like word is no
longer mistaken for a quote-like operator (s///, m//, tr///, ...).
Likewise a package-qualified call such as "mb::tr($x, 'A', '1')" is
no longer read as the tr/// transliteration operator: the masker now
refuses to start a quote-like operator when the preceding character
is '*', '&', or ':'.
- Fixed false positive: the old-style apostrophe package separator
(e.g. "&jcode'tr(...)", equivalent to "&jcode::tr(...)") is now
recognized. A sigil-led identifier followed by "'" and a word
character is treated as a package separator, not the start of a
single-quoted string, so the remainder of the line no longer desyncs.
- Fixed false positive: an escaped backslash followed by a regex-escape
letter inside a regex literal, e.g. the two-character literal "\\h"
in "(?:(?![$bare_h])$x)" style replacement strings, is no longer
mistaken for the Perl 5.10 \h horizontal-whitespace escape. Backslash
pairs are neutralized before the regex-body scan.
- Fixed false positive: a postfix-increment operator on a
package-qualified or subscripted lvalue (e.g. "$mb::seq++") is no
longer reported as a Perl 5.10 possessive quantifier; "++" in masked
code is always the increment operator (a genuine possessive
quantifier can occur only inside a regex, which is scanned separately).
- Added regression tests for the above fixes in t/0008-new-features.t.
- Fixed false positive: the empty-import form "use feature ()" is no
longer reported as the Perl 5.10 "use feature" pragma. Used with the
"BEGIN { $INC{'feature.pm'} = '' if $] < 5.010 }" stub it imports and
loads nothing on Perl 5.005_03 (a no-op on every Perl version), so it
is the standard cross-version guard idiom. A non-empty import list
such as "use feature 'say'" or "use feature qw(...)" is still
reported. Regression tests added in t/0002-check-source.t.
- Added t/0009-clean-corpus.t: a clean-corpus regression test that
scans the shipped library modules (lib/*.pm) of eight known
5.005_03-compatible ina@CPAN distributions -- CSV-LINQ, LTSV-LINQ,
JSON-LINQ, mb-JSON, Modern-Open, Perl7-Handy, BATsh and UTF8-R2 --
and asserts zero violations for each. The sample sources live under
t/corpus/. This guards against future BLACKLIST/masker changes
introducing false positives against genuine clean code.
- Added t/0010-clean-corpus-stack.t: a second clean-corpus regression
test scanning the shipped library modules (lib/*.pm) of eight more
5.005_03-compatible ina@CPAN distributions -- the Handy runtime
stack (PSGI-Handy, HTTP-Handy, DB-Handy, HP-Handy) and the
character-encoding libraries (Jacode, mb, Jacode4e,
Jacode4e-RoundTrip) -- and asserting zero violations for each (12
module files). The sample sources live under t/corpus-stack/, a
sibling of t/corpus/ so the 0009 walker does not descend into it.
Three of the sources (mb.pm, Jacode4e.pm, Jacode4e/RoundTrip.pm) are
multibyte (MBCS); they are read with binmode and exercise the masker
against large, real-world non-ASCII code.
- t/9010-encoding.t: the t/corpus and t/corpus-stack trees are now
exempted from the encoding-hygiene checks, the same kind of exemption
already granted to doc/. External corpus sources are stored byte-exact
and are not held to OrDie's own US-ASCII house rule. This also avoids
a false positive in the C2 (trailing-whitespace) check: a legacy
multibyte source whose double-byte character ends in 0x20 or 0x09 just
before a newline would otherwise be flagged by the byte-pattern
/[ \t]+\n/ even though no real trailing whitespace exists. The corpus
trees are matched directly from the MANIFEST list with
!m{^t/corpus(?:-stack)?/}; their 5.005_03 compatibility is validated
by t/0009-clean-corpus.t and t/0010-clean-corpus-stack.t.
- t/9020-perl5compat.t: the corpus exemption regex was widened from
[/\\]corpus[/\\] to [/\\]corpus(?:-stack)?[/\\] so that the sibling
t/corpus-stack tree is excluded as well. Without this the external
samples (e.g. HTTP::Handy and mb, which use a scalar filehandle in
open()) were wrongly held to this distribution's bareword-filehandle
(P14) house rule.
0.02 2026-05-29 JST (Japan Standard Time)
- Fixed malformed POD interior sequences (C<<<>>>, C<<<~>) that Pod::Checker 1.51-1.73 flagged as errors on perl 5.16-5.24
0.01 2026-05-24 JST (Japan Standard Time)
- original version
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)