add definition_context info for inlined constructors and destructors
Jesse Luehrs [Sun, 24 Apr 2011 16:12:25 +0000 (11:12 -0500)]
lib/Class/MOP/Attribute.pm
lib/Class/MOP/Class.pm
lib/Moose/Meta/Method/Constructor.pm
lib/Moose/Meta/Method/Destructor.pm
t/immutable/definition_context.t [new file with mode: 0644]

index 808cb0f..d16d66f 100644 (file)
@@ -378,7 +378,7 @@ sub _process_accessors {
         my $method;
         try {
             if ( $method_ctx ) {
-                my $desc = "accessor $accessor";
+                my $desc = "accessor " . $self->associated_class->name . "::$accessor";
                 if ( $accessor ne $self->name ) {
                     $desc .= " of attribute " . $self->name;
                 }
index 8aa4170..9fc62b3 100644 (file)
@@ -1256,8 +1256,13 @@ sub _immutable_options {
 sub make_immutable {
     my ( $self, @args ) = @_;
 
+    my ($file, $line) = (caller)[1..2];
     if ( $self->is_mutable ) {
-        $self->_initialize_immutable( $self->_immutable_options(@args) );
+        $self->_initialize_immutable(
+            $self->_immutable_options(@args),
+            file => $file,
+            line => $line,
+        );
         $self->_rebless_as_immutable(@args);
         return $self;
     }
@@ -1413,6 +1418,11 @@ sub _inline_constructor {
         is_inline    => 1,
         package_name => $self->name,
         name         => $name,
+        definition_context => {
+            description => "constructor " . $self->name . "::" . $name,
+            file        => $args{file},
+            line        => $args{line},
+        },
     );
 
     if ( $args{replace_constructor} or $constructor->can_be_inlined ) {
@@ -1445,7 +1455,12 @@ sub _inline_destructor {
         options      => \%args,
         metaclass    => $self,
         package_name => $self->name,
-        name         => 'DESTROY'
+        name         => 'DESTROY',
+        definition_context => {
+            description => "destructor " . $self->name . "::DESTROY",
+            file        => $args{file},
+            line        => $args{line},
+        },
     );
 
     if ( $args{replace_destructor} or $destructor->can_be_inlined ) {
index bc20c51..695b826 100644 (file)
@@ -29,6 +29,7 @@ sub new {
         'name'          => $options{name},
         'options'       => $options{options},
         'associated_metaclass' => $meta,
+        'definition_context' => $options{definition_context},
         '_expected_method_class' => $options{_expected_method_class} || 'Moose::Object',
     } => $class;
 
index 2ce956d..1c3ceca 100644 (file)
@@ -28,6 +28,7 @@ sub new {
         'name'                 => $options{name},
         # ...
         'options'              => $options{options},
+        'definition_context'   => $options{definition_context},
         'associated_metaclass' => $options{metaclass},
     } => $class;
 
diff --git a/t/immutable/definition_context.t b/t/immutable/definition_context.t
new file mode 100644 (file)
index 0000000..de82c88
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+
+{
+    use Moose::Util::TypeConstraints;
+    use Carp 'confess';
+    subtype 'Death', as 'Int', where { $_ == 1 };
+    coerce  'Death', from 'Any', via { confess };
+}
+
+{
+    my ($attr_foo_line, $attr_bar_line, $ctor_line);
+    {
+        package Foo;
+        use Moose;
+
+        has foo => (
+            is     => 'rw',
+            isa    => 'Death',
+            coerce => 1,
+        );
+        $attr_foo_line = __LINE__ - 5;
+
+        has bar => (
+            accessor => 'baz',
+            isa      => 'Death',
+            coerce   => 1,
+        );
+        $attr_bar_line = __LINE__ - 5;
+
+        __PACKAGE__->meta->make_immutable;
+        $ctor_line = __LINE__ - 1;
+    }
+
+    like(
+        exception { Foo->new(foo => 2) },
+        qr/called at constructor Foo::new \(defined at $0 line $ctor_line\)/,
+        "got definition context for the constructor"
+    );
+
+    like(
+        exception { my $f = Foo->new(foo => 1); $f->foo(2) },
+        qr/called at accessor Foo::foo \(defined at $0 line $attr_foo_line\)/,
+        "got definition context for the accessor"
+    );
+
+    like(
+        exception { my $f = Foo->new(foo => 1); $f->baz(2) },
+        qr/called at accessor Foo::baz of attribute bar \(defined at $0 line $attr_bar_line\)/,
+        "got definition context for the accessor"
+    );
+}
+
+{
+    my ($dtor_line);
+    {
+        package Bar;
+        use Moose;
+
+        # just dying here won't work, because perl's exception handling is
+        # terrible
+        sub DEMOLISH { try { confess } catch { warn $_ } }
+
+        __PACKAGE__->meta->make_immutable;
+        $dtor_line = __LINE__ - 1;
+    }
+
+    {
+        my $warning = '';
+        local $SIG{__WARN__} = sub { $warning .= $_[0] };
+        { Bar->new }
+        like(
+            $warning,
+            qr/called at destructor Bar::DESTROY \(defined at $0 line $dtor_line\)/,
+            "got definition context for the destructor"
+        );
+    }
+}
+
+done_testing;