The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

our $ran = 0;

sub _eval {
    my $code = shift;

    local $@;

    # double eval necessary for cleanup-time bugs
    my $ok = eval qq{
        use Lexical::SingleAssignment;
        $code;
        1;
    };

    return $ok ? '' : ( $@ || "unknown error" );
}

sub eval_ok {
    my ( $code, @args ) = @_;
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    &is( _eval($code), '', @args );
}

sub eval_nok {
    my ( $code, $re, @args ) = @_;

    $re ||= qr/./;

    local $Test::Builder::Level = $Test::Builder::Level + 1;
    &like( _eval($code), $re, @args );
}

eval_ok q{
    my $x = 4;
}, "sassign";

eval_ok q{
    my $x = 4;
    is( $x, 4, "sassign still works" );
}, "sassign";

eval_ok q{
    my $x = rand;
    ok( defined($x), "sassign still works" );
}, "sassign";

eval_ok q{
    my @y = qw(foo bar);
}, "simple aassign";

eval_ok q{
    my @y = qw(foo bar);
    is( scalar(@y), 2, "aassign still works" );
}, "simple aassign";

eval_ok q{
    my ( $x, @y ) = qw(foo bar);

    is( $x, "foo", "compound aasign still works");
    is( $y[0], "bar", "compound aassign still works");
}, "compound aassign";

eval_nok q{
    my $x = 4;
    $x = 5;
}, qr/declaration/, "sassign post declaration";

eval_nok q{
    my ( $x, @y ) = qw(foo bar);
    $x = "bar";
}, qr/declaration/, "sassign post declaration";

eval_nok q{
    my ( $x, @y ) = qw(foo bar);
    ( $x, @y ) = qw(bar foo);
}, qr/declaration/, "aassign post declaration";

eval_nok q{
    my $x = "foo";
    my $y = "bar";

    ( $x, $y ) = qw(bar foo);
}, qr/declaration/, "aassign post declaration";



eval_nok q{
    my ( $x, @y ) = qw(foo bar);

    $ran++;

    $y[0] = "foo";
}, qr/./, "aassign creates readonly array elements";

{
    local $TODO = "can't detect assignment to subscript over AV/HV yet, runtime error instead";
    is( $ran, 0, "caught at compile time" );
}




$ran = 0;

eval_nok q{
    my ( $x, @y ) = qw(foo bar);

    $ran++;

    pop @y;
}, qr/./, "aassign creates readonly arrays";

{
    local $TODO = "can't detect assignment to subscript over AV/HV yet, runtime error instead";
    is( $ran, 0, "caught at compile time" );
}


$ran = 0;

eval_nok q{
    my ( $x, %z ) = qw(foo bar baz);

    $ran++;

    $z{bar} = "foo";
}, qr/./, "aassign creates readonly hash elements";

{
    local $TODO = "can't detect assignment to subscript over AV/HV yet, runtime error instead";
    is( $ran, 0, "caught at compile time" );
}


$ran = 0;

eval_nok q{
    my ( $x, %z ) = qw(foo bar baz);

    $ran++;

    $z{new_key} = "foo";
}, qr/./, "aassign creates readonly hashes";

{
    local $TODO = "can't detect assignment to subscript over AV/HV yet, runtime error instead";
    is( $ran, 0, "caught at compile time" );
}



eval_nok q{
    my $x = 3;
    my $ref = \$x;
    $$ref = 3;
}, qr/read-only/, "sassign creates readonly scalars (by ref)";

eval_nok q{
    my ( $x, @y ) = qw(foo bar);

    my $ref = \$x;
    $$ref = "bar";
}, qr/read-only/, "aassign creates readonly scalars (by ref)";

eval_nok q{
    my ( $x, @y ) = qw(foo bar);

    my $ref = \@y;
    pop @$ref;
}, qr/read-only/, "aassign creates readonly arrays (by ref)";

eval_nok q{
    my ( $x, @y ) = qw(foo bar);

    my $ref = \@y;
    $ref->[0] = "foo";
}, qr/read-only/, "aassign creates readonly array elements (by ref)";

eval_nok q{
    my $x;
}, qr/lexical without assignment/, "declaration without assignment";

eval_nok q{
    my @y;
}, qr/lexical without assignment/, "declaration without assignment";

eval_nok q{
    my %h;
}, qr/lexical without assignment/, "declaration without assignment";

eval_ok q{
    {
        no Lexical::SingleAssignment;

        my $x;

        $x = 3;
    }
}, "unimport";

eval_nok q{
    my $x = 5;
    {
        no Lexical::SingleAssignment;

        $x = 3;
    }
}, qr/read-only/, "unimport, assignment to var declared in scope";

eval_nok q{
    my $x = 3;

    BEGIN { die "foo" }
}, qr/foo/, "unrelated errors pass through";

eval_nok q{
    my $x;

    BEGIN { die "foo" }
}, qr/foo/, "delayed padsv check does not clobber errors";

done_testing;

# ex: set sw=4 et: