Skip to content

Commit f1b09e7

Browse files
committed
reset line numbers after inlining type constraints
Otherwise multi-line code returned from TC->inline_check will bump line numbers for the rest of the file, as if the code had been included literally. Fixes #42 (GH).
1 parent 02ac365 commit f1b09e7

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

MANIFEST

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ t/types_custom.t
7878
t/types_custom_2.t
7979
t/types_custom_3.t
8080
t/types_custom_4.t
81+
t/types_inline.t
8182
t/types_moose.t
8283
t/types_moose_2.t
8384
t/types_moose_3.t

Parameters.xs

+6-3
Original file line numberDiff line numberDiff line change
@@ -999,19 +999,22 @@ static OP *mktypecheckv(pTHX_ Sentinel sen, const SV *declarator, size_t nr, SV
999999
ENTER;
10001000
SAVETMPS;
10011001

1002+
SAVECOPLINE(PL_curcop);
1003+
10021004
lex_start(src, NULL, 0);
10031005
chk = parse_fullexpr(0);
10041006
if (PL_parser->error_count) {
10051007
op_free(chk);
10061008
chk = NULL;
10071009
}
1008-
if (!chk) {
1009-
Perl_croak(aTHX_ "In %"SVf": inlining type constraint %"SVf" for %s %lu (%"SVf") failed", SVfARG(declarator), SVfARG(type), is_invocant ? "invocant" : "parameter", (unsigned long)nr, SVfARG(name));
1010-
}
10111010

10121011
FREETMPS;
10131012
LEAVE;
10141013

1014+
if (!chk) {
1015+
Perl_croak(aTHX_ "In %"SVf": inlining type constraint %"SVf" for %s %lu (%"SVf") failed", SVfARG(declarator), SVfARG(type), is_invocant ? "invocant" : "parameter", (unsigned long)nr, SVfARG(name));
1016+
}
1017+
10151018
if (has_coercion) {
10161019
OP *args2 = NULL, *coerce;
10171020

t/types_inline.t

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!perl
2+
use warnings qw(all FATAL uninitialized);
3+
use strict;
4+
use Test::More tests => 14;
5+
use Test::Fatal;
6+
use Function::Parameters;
7+
8+
BEGIN {
9+
package MyTC;
10+
11+
method new( $class:
12+
:$incline = 0,
13+
:$file = undef,
14+
:$line = undef,
15+
:$broken = undef,
16+
) {
17+
bless {
18+
incline => $incline,
19+
file => $file,
20+
line => $line,
21+
broken => $broken,
22+
}, $class
23+
}
24+
25+
method can_be_inlined() {
26+
1
27+
}
28+
29+
method inline_check($var) {
30+
my $line = $self->{line};
31+
my $file = $self->{file};
32+
if (defined $file) {
33+
$line //= (caller)[2];
34+
}
35+
my $header = defined $line ? qq{#line $line "$file"\n} : "";
36+
my $garbage = ";\n" x $self->{incline};
37+
my $error = $self->{broken} ? "]" : "";
38+
$header . "do { $garbage defined($var) $error }"
39+
}
40+
41+
method check($value) {
42+
die "check() shouldn't be called";
43+
}
44+
45+
method get_message($value) {
46+
"value is not defined"
47+
}
48+
}
49+
50+
use constant {
51+
TDef => MyTC->new,
52+
TBroken => MyTC->new(broken => 1, incline => 99),
53+
TDefI7 => MyTC->new(incline => 7),
54+
TDefX => MyTC->new(file => "fake-file", line => 666_666),
55+
TDefXI2 => MyTC->new(file => "fake-file", line => 666_666, incline => 2),
56+
};
57+
58+
is eval(qq|#line 2 "(virtual)"\nfun (TBroken \$bad) {}|), undef, "broken type constraint doesn't compile";
59+
like $@, qr/\binlining type constraint MyTC=HASH\(\w+\) for parameter 1 \(\$bad\) failed at \(virtual\) line 2\b/, "broken type constraint reports correct source location";
60+
61+
#line 62 "t/types_inline.t"
62+
fun foo0(TDef $x) { $x }
63+
64+
is foo0('good'), 'good', "defined value passes inline check";
65+
like exception { foo0(undef) }, qr/\AIn fun foo0: parameter 1 \(\$x\): value is not defined\b/, "undefined value throws";
66+
is __FILE__ . ' ' . __LINE__, "t/types_inline.t 66", "source location OK";
67+
68+
#line 69 "t/types_inline.t"
69+
fun foo1(TDefI7 $x) { $x }
70+
71+
is foo1('good'), 'good', "(+7) defined value passes inline check";
72+
like exception { foo1(undef) }, qr/\AIn fun foo1: parameter 1 \(\$x\): value is not defined\b/, "(+7) undefined value throws";
73+
is __FILE__ . ' ' . __LINE__, "t/types_inline.t 73", "(+7) source location OK";
74+
75+
#line 76 "t/types_inline.t"
76+
fun foo2(TDefX $x) { $x }
77+
78+
is foo2('good'), 'good', "(X) defined value passes inline check";
79+
like exception { foo2(undef) }, qr/\AIn fun foo2: parameter 1 \(\$x\): value is not defined\b/, "(X) undefined value throws";
80+
is __FILE__ . ' ' . __LINE__, "t/types_inline.t 80", "(X) source location OK";
81+
82+
#line 83 "t/types_inline.t"
83+
fun foo3(TDefXI2 $x) { $x }
84+
85+
is foo3('good'), 'good', "(X+2) defined value passes inline check";
86+
like exception { foo3(undef) }, qr/\AIn fun foo3: parameter 1 \(\$x\): value is not defined\b/, "(X+2) undefined value throws";
87+
is __FILE__ . ' ' . __LINE__, "t/types_inline.t 87", "(X+2) source location OK";

0 commit comments

Comments
 (0)