| File: | blib/lib/App/Test/Generator/Analyzer/Return.pm |
| Coverage: | 92.3% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package App::Test::Generator::Analyzer::Return; | |||||
| 2 | ||||||
| 3 | 31 31 31 | 69487 26 400 | use strict; | |||
| 4 | 31 31 31 | 50 22 551 | use warnings; | |||
| 5 | 31 31 31 | 58 29 525 | use Carp qw(croak); | |||
| 6 | 31 31 31 | 56 25 490 | use Readonly; | |||
| 7 | 31 31 31 | 53 20 6818 | use Scalar::Util qw(blessed); | |||
| 8 | ||||||
| 9 | # -------------------------------------------------- | |||||
| 10 | # Evidence weights for each detected return pattern. | |||||
| 11 | # Higher weights indicate stronger signals that the | |||||
| 12 | # detected pattern is the primary return behaviour. | |||||
| 13 | # -------------------------------------------------- | |||||
| 14 | Readonly my $WEIGHT_RETURNS_PROPERTY => 20; | |||||
| 15 | Readonly my $WEIGHT_RETURNS_SELF => 15; | |||||
| 16 | Readonly my $WEIGHT_RETURNS_CONSTANT => 10; | |||||
| 17 | ||||||
| 18 | our $VERSION = '0.41'; | |||||
| 19 | ||||||
| 20 - 59 | =head1 VERSION
Version 0.41
=head1 DESCRIPTION
Analyses the source code of a method and adds evidence to a
L<App::Test::Generator::Model::Method> object describing what kind of
value the method returns. Evidence is used downstream by
L<App::Test::Generator::Model::Method/resolve_return_type> to determine
the most likely return type.
=head2 new
Construct a new Return analyser.
my $analyser = App::Test::Generator::Analyzer::Return->new;
=head3 Arguments
None.
=head3 Returns
A blessed hashref.
=head3 API specification
=head4 input
{}
=head4 output
{
type => OBJECT,
isa => 'App::Test::Generator::Analyzer::Return',
}
=cut | |||||
| 60 | ||||||
| 61 | sub new { | |||||
| 62 | 316 | 111772 | my $class = $_[0]; | |||
| 63 | 316 | 313 | return bless {}, $class; | |||
| 64 | } | |||||
| 65 | ||||||
| 66 - 118 | =head2 analyze
Scan the source code of a method for return patterns and add weighted
evidence to the method object. Detects three patterns: returning a
property from C<$self>, returning C<$self> itself, and returning a
constant literal value.
my $analyser = App::Test::Generator::Analyzer::Return->new;
$analyser->analyze($method);
my $type = $method->resolve_return_type;
=head3 Arguments
=over 4
=item * C<$method>
Normally an L<App::Test::Generator::Model::Method> object; evidence is
added to it in place via C<add_evidence>. A plain hashref with a
C<source> or C<body> key is also accepted defensively (as SchemaExtractor
callers may pass one), but in that case no evidence is recorded â only
a Model::Method object exposing both C<source()> and C<add_evidence()>
actually accumulates evidence.
=back
=head3 Returns
Nothing (undef). All results are communicated via side effects on the
C<$method> object.
=head3 Notes
The interface of this analyser differs from
L<App::Test::Generator::Analyzer::ReturnMeta>, which operates on a raw
schema hashref. This analyser operates on a C<Model::Method> object
directly.
=head3 API specification
=head4 input
{
self => { type => OBJECT, isa => 'App::Test::Generator::Analyzer::Return' },
method => { type => OBJECT, isa => 'App::Test::Generator::Model::Method' },
}
=head4 output
{ type => UNDEF }
=cut | |||||
| 119 | ||||||
| 120 | sub analyze { | |||||
| 121 | 316 | 2063 | my ($self, $method) = @_; | |||
| 122 | ||||||
| 123 | # Accept either a Model::Method object or a raw hashref, | |||||
| 124 | # since callers in SchemaExtractor pass raw hashrefs | |||||
| 125 | my $source = blessed($method) && $method->can('source') | |||||
| 126 | ? $method->source() | |||||
| 127 | 316 | 1185 | : ($method->{source} // $method->{body} // ''); | |||
| 128 | ||||||
| 129 | my $add = blessed($method) && $method->can('add_evidence') | |||||
| 130 | 219 | 237 | ? sub { $method->add_evidence(@_) } | |||
| 131 | 316 | 1086 | : sub {}; | |||
| 132 | ||||||
| 133 | # -------------------------------------------------- | |||||
| 134 | # Detect: return $self->{property} | |||||
| 135 | # Negative lookahead ensures this does not also match | |||||
| 136 | # plain return $self (handled separately below). | |||||
| 137 | # Matched with /g so a method with several differing | |||||
| 138 | # property-returning branches contributes evidence for | |||||
| 139 | # each one found, not just the first in the source. | |||||
| 140 | # -------------------------------------------------- | |||||
| 141 | 316 | 519 | while($source =~ /return\s+\$self->\{(\w+)\}/g) { | |||
| 142 | 39 | 67 | $add->( | |||
| 143 | category => 'return', | |||||
| 144 | signal => 'returns_property', | |||||
| 145 | value => $1, | |||||
| 146 | weight => $WEIGHT_RETURNS_PROPERTY, | |||||
| 147 | ); | |||||
| 148 | } | |||||
| 149 | ||||||
| 150 | # -------------------------------------------------- | |||||
| 151 | # Detect: return $self | |||||
| 152 | # \b after \$self prevents matching variable names that | |||||
| 153 | # merely start with "self" (e.g. $self_backup, $selfish); | |||||
| 154 | # the negative lookahead then excludes return $self->{...} | |||||
| 155 | # which is a property return handled above. Matched with | |||||
| 156 | # /g for the same multi-occurrence reason as above. | |||||
| 157 | # -------------------------------------------------- | |||||
| 158 | 316 | 559 | while($source =~ /return\s+\$self\b(?!->)/g) { | |||
| 159 | 13 | 20 | $add->( | |||
| 160 | category => 'return', | |||||
| 161 | signal => 'returns_self', | |||||
| 162 | weight => $WEIGHT_RETURNS_SELF, | |||||
| 163 | ); | |||||
| 164 | } | |||||
| 165 | ||||||
| 166 | # -------------------------------------------------- | |||||
| 167 | # Detect: return of a constant literal â quoted string, | |||||
| 168 | # numeric literal, or undef. All indicate the method | |||||
| 169 | # returns a fixed value rather than a computed state. | |||||
| 170 | # Matched with /g for the same multi-occurrence reason | |||||
| 171 | # as the patterns above. | |||||
| 172 | # -------------------------------------------------- | |||||
| 173 | 316 | 610 | while($source =~ /return\s+(?:['"\d]|undef\b)/g) { | |||
| 174 | 170 | 174 | $add->( | |||
| 175 | category => 'return', | |||||
| 176 | signal => 'returns_constant', | |||||
| 177 | weight => $WEIGHT_RETURNS_CONSTANT, | |||||
| 178 | ); | |||||
| 179 | } | |||||
| 180 | ||||||
| 181 | 316 | 633 | return; | |||
| 182 | } | |||||
| 183 | ||||||
| 184 - 192 | =head1 LICENCE AND COPYRIGHT Copyright 2025-2026 Nigel Horne. Usage is subject to GPL2 licence terms. If you use it, please let me know. =cut | |||||
| 193 | ||||||
| 194 | 1; | |||||