package Perl::Critic::Policy::Community::EmptyReturn;

use strict;
use warnings;

use Perl::Critic::Utils qw(:severities :classification :ppi);
use parent 'Perl::Critic::Policy';

use List::Util 'any';
use Perl::Critic::Community::Utils qw(is_empty_return is_structural_block);

our $VERSION = 'v1.0.0';

use constant DESC => 'return called with no arguments';
use constant EXPL => 'return with no arguments may return either undef or an empty list depending on context. This can be surprising for the same reason as other context-sensitive returns. Return undef or the empty list explicitly.';

sub supported_parameters { () }
sub default_severity { $SEVERITY_LOWEST }
sub default_themes { 'community' }
sub applies_to { 'PPI::Statement::Sub' }

sub violates {
	my ($self, $elem) = @_;
	
	my $block = $elem->block || return ();
	my $returns = $block->find(sub {
		my ($elem, $child) = @_;
		# Don't search in blocks unless we know they are structural
		if ($child->isa('PPI::Structure::Block')) {
			return undef unless is_structural_block($child);
		}
		return 1 if $child->isa('PPI::Token::Word') and $child eq 'return';
		return 0;
	});
	
	# Return a violation for each empty return, if any non-empty return is present
	if ($returns and any { !is_empty_return($_) } @$returns) {
		return map { $self->violation(DESC, EXPL, $_) } grep { is_empty_return($_) } @$returns;
	}
	
	return ();
}

1;

=head1 NAME

Perl::Critic::Policy::Community::EmptyReturn - Don't use return with no
arguments

=head1 DESCRIPTION

Context-sensitive functions, while one way to write functions that DWIM (Do
What I Mean), tend to instead lead to unexpected behavior when the function is
accidentally used in a different context, especially if the function's behavior
changes significantly based on context. This also can lead to vulnerabilities
when a function is intended to be used as a scalar, but is used in a list, such
as a hash constructor or function parameter list. C<return> with no arguments
will return either C<undef> or an empty list depending on context. Instead,
return the appropriate value explicitly.

  return;       # not ok
  return ();    # ok
  return undef; # ok

  sub get_stuff {
    return unless @things;
    return join(' ', @things);
  }
  my %stuff = (
    one => 1,
    two => get_stuff(), # oops! function returns empty list if @things is empty
    three => 3,
  );

Empty returns are permitted by this policy if the subroutine contains no
explicit return values, indicating it is intended to be used in void context.

=head1 CAVEATS

This policy currently only checks return statements in named subroutines,
anonymous subroutines are not checked. Also, return statements within blocks,
other than compound statements like C<if> and C<foreach>, are not considered
when determining if a function is intended to be used in void context.

Any non-empty return will cause empty returns within the same subroutine to
report violations, even though in list context, C<return> and C<return ()> are
functionally equivalent. It is recommended to explicitly specify an empty list
return with C<return ()> in a function that intends to return list context.

=head1 AFFILIATION

This policy is part of L<Perl::Critic::Community>.

=head1 CONFIGURATION

This policy is not configurable except for the standard options.

=head1 AUTHOR

Dan Book, C<dbook@cpan.org>

=head1 COPYRIGHT AND LICENSE

Copyright 2015, Dan Book.

This library is free software; you may redistribute it and/or modify it under
the terms of the Artistic License version 2.0.

=head1 SEE ALSO

L<Perl::Critic>