package Perl::Critic::Policy::Objects::ProhibitIndirectSyntax; use 5.010001; use strict; use warnings; use Perl::Critic::Utils qw{ :severities :classification }; use Readonly; use parent 'Perl::Critic::Policy'; our $VERSION = '1.152'; #----------------------------------------------------------------------------- Readonly::Hash my %COMMA => { q<,> => 1, q{=>} => 1, }; Readonly::Scalar my $DOLLAR => q<$>; Readonly::Scalar my $DESC => 'Subroutine "%s" called using indirect syntax'; Readonly::Scalar my $EXPL => [ 349 ]; #----------------------------------------------------------------------------- sub supported_parameters { return ( { name => 'forbid', description => 'Indirect method syntax is forbidden for these methods.', behavior => 'string list', list_always_present_values => [ qw{ new } ], } ); } sub default_severity { return $SEVERITY_HIGH } sub default_themes { return qw( core pbp maintenance certrule ) } sub applies_to { return 'PPI::Token::Word' } #----------------------------------------------------------------------------- sub violates { my ( $self, $elem, $doc ) = @_; # We are only interested in the functions we have been told to check. # Do this before calling is_function_call() because we want to weed # out as many candidate tokens as possible before calling it. return if not $self->{_forbid}->{$elem->content()}; # Make sure it really is a function call. return if not is_function_call($elem); # Per perlobj, it is only an indirect object call if the next sibling # is a word, a scalar symbol, or a block. my $object = $elem->snext_sibling() or return; return if not ( $object->isa( 'PPI::Token::Word' ) or $object->isa( 'PPI::Token::Symbol' ) and $DOLLAR eq $object->raw_type() or $object->isa( 'PPI::Structure::Block' ) ); # Per perlobj, it is not an indirect object call if the operator after # the possible indirect object is a comma. if ( my $operator = $object->snext_sibling() ) { return if $operator->isa( 'PPI::Token::Operator' ) and $COMMA{ $operator->content() }; } my $message = sprintf $DESC, $elem->content(); return $self->violation( $message, $EXPL, $elem ); } 1; __END__ #----------------------------------------------------------------------------- =pod =head1 NAME Perl::Critic::Policy::Objects::ProhibitIndirectSyntax - Prohibit indirect object call syntax. =head1 AFFILIATION This Policy is part of the core L distribution. =head1 DESCRIPTION Indirect object syntax is commonly used in other object-oriented languages for instantiating objects. Perl allows this, but to say that it supports it may be going too far. Instead of writing my $foo = new Foo; it is preferable to write my $foo = Foo->new; The problem is that Perl needs to make a number of assumptions at compile time to disambiguate the first form, so it tends to be fragile and to produce hard-to-track-down bugs. =head1 CONFIGURATION Indirect object syntax is also hard for Perl::Critic to disambiguate, so this policy only checks certain subroutine calls. The names of the subroutines can be configured using the C configuration option: [Objects::ProhibitIndirectSyntax] forbid = create destroy The C subroutine is configured by default; any additional C values are in addition to C. =head1 CAVEATS The general situation can not be handled via static analysis. =head1 SEE ALSO L and L both do a better job with this, but they require that you compile/execute your code. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT Copyright (c) 2009-2023 Tom Wyant This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of this license can be found in the LICENSE file included with this module. =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 78 # indent-tabs-mode: nil # c-indentation-style: bsd # End: # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :