The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Perl::Critic::Policy::Variables::ProhibitUnusedVarsStricter - Don't ask for storage you don't need.

AFFILIATION

This Policy is stand-alone, and is not part of the core Perl::Critic.

NOTE

As of version 0.099_001, the logic that recognizes variables interpolated into double-quotish strings has been refactored into module PPIx::QuoteLike.

DESCRIPTION

Unused variables clutter code and require the reader to do mental bookkeeping to figure out if the variable is actually used or not.

Right now, this only looks for lexical variables which are unused other than in the statement that declares them.

    my $x;          # not ok, assuming no other appearances.
    my @y = ();     # not ok, assuming no other appearances.
    our $z;         # ok, global.
    local $w;       # ok, global.

This policy is a variant on the core policy Perl::Critic::Policy::Variables::ProhibitUnusedVariables which attempts to be more strict in its checking of whether a variable is used. The specific differences are:

* An attempt is made to take into account the scope of the declaration.

* An attempt is made to find variables which are interpolated into double-quotish strings (including regexes) and here documents.

* An attempt is made to find variables which are used in regular expression (?{...}) and (??{...}) constructions, and in the replacement portion of s/.../.../e.

* An attempt is made to find variables which are used in subroutine signatures.

This policy intentionally does not report variables as unused if the code takes a reference to the variable, even if it is otherwise unused. For example things like

    \( my $foo = 'bar' )
    \do{ my $foo => 'bar' }

will not be reported as a violation even if $foo is otherwise unused. The reason is that this is an idiom for making a reference to a mutable string when all you have is an immutable string. This policy does not check to see if anything is done with the reference.

This policy also does not detect unused variables declared inside various odd corners such as

    s///e
    qr{(?{...})}
    qr{(??{...})}
    "@{[ ... ]}"
    ( $foo, my $bar ) = ( 1, 2 )
    sub ( $foo = $bar ) { ... } # Signature, not prototype

Most of these are because the PPI parse of the original document does not include the declarations. The list assignment is missed because PPI does not parse it as containing a PPI::Statement::Variable. However, variables used inside such constructions will be detected.

CONFIGURATION

This policy supports the following configuration items:

allow_unused_subroutine_arguments

By default, this policy prohibits unused subroutine arguments -- that is, unused variables on the right-hand side of such simple assignments as

    my ( $foo ) = @_;
    my $bar     = shift;
    my $baz     = shift @_;
    my $burfle  = $_[0];

If you wish to allow unused variables in this case, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    allow_unused_subroutine_arguments = 1

prohibit_reference_only_variables

By default, this policy allows otherwise-unused variables if the code takes a reference to the variable when it is created. If you wish to declare a violation in this case, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    prohibit_reference_only_variables = 1

prohibit_returned_lexicals

By default, this policy allows otherwise-unused lexical variables (either 'my' or 'state') if they are being returned from a subroutine, under the presumption that they are going to be used as lvalues by the caller. If you wish to declare a violation in this case, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    prohibit_returned_lexicals = 1

Note that only explicit 'return' statements are covered by this setting. No attempt is made to detect and allow implicit returns.

allow_if_computed_by

You may wish to allow variables to be unused when computed in certain ways. For example, you might want to allow place-holder variables in a list computed by stat() or unpack(). Or you may be doing end-of-scope detection with something like my $foo = Scope::Guard->new( \&end_of_scope ). To ignore all these, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    allow_if_computed_by = stat unpack Scope::Guard

This property takes as its value a whitespace-delimited list of class or subroutine names. Nothing complex is done to implement this -- the policy simply looks at the first word after the equals sign, if any.

allow_state_in_expression

By default, this policy handles state variables as any other lexical, and a violation is declared if they appear only in the statement that declares them.

One might, however, do something like

    state $foo = compute_foo() or do_something_else();

In this case, compute_foo() is called only once, but if it returns a false value do_something_else() will be executed every time this statement is encountered.

If you wish to allow such code, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    allow_state_in_expression = 1

This allows an otherwise-unused state variable if its value appears to be used in an expression -- that is, if its declaration is followed by a low-precedence boolean, or one of its ancestors is preceded or followed by any operator. The latter means that something like

 my $bar = ( state $foo = compute_foo() ) + 42;

will be accepted.

This will also cause post-increment and post-decrement to be accepted, allowing code like

 do_something() unless state $foo++;

Pre-increment and pre-decrement are ignored, since as of Perl 5.34.0 they are syntax errors.

check_catch

Under ordinary circumstances the $err variable in

 try {
   ...
 } catch ( $err ) {
   ...
 }

will be invisible to this policy because, although it is in fact the declaration of a lexical variable, the absence of a my means it does not look like one to PPI. If you want to test these, you can add a block like this to your .perlcriticrc file:

    [Variables::ProhibitUnusedVarsStricter]
    check_catch = 1

This option is not on by default because there appears to be no way to define a catch() block without a variable, whether or not you intend to use it.

Caveat: if PPI ever starts recognizing catch( $foo ) as containing a PPI::Statement::Variable, this configuration variable will become moot, as the extra logic will no longer be needed. As soon as I recognize this has happened (and there is an author test for it) I will document this configuration item as a no-op, deprecate it, and probably eventually retract it.

AVOIDING UNUSED VARIABLES

There are situations in Perl where eliminating unused variables is less straightforward than simply deleting them:

List Assignments

This situation typically (I believe) comes up when your code gets handed a list, and is not interested in all values in the list. You could, of course, assign the whole thing to an array and then cherry-pick the array, but there are less-obvious solutions that avoid the extra assignment.

For the purpose of this discussion, I will assume code which calls the localtime() built-in, but is only interested in day, month, and year. The cut-and-paste implementation (direct from perldoc -f localtime, or https://perldoc.pl/functions/localtime if you prefer) is:

    #     0    1    2     3     4    5     6     7     8
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        localtime();

Now, you can trivially eliminate the variables after $year, but that still leaves

    #     0    1    2     3     4    5
    my ($sec,$min,$hour,$mday,$mon,$year) = localtime();

with $sec, $min, and $hour assigned-to but unused. There are two ways I know of to eliminate these:

Assign to undef

On the left-hand side of a list assignment, undef causes the corresponding right-hand value to be ignored. This makes our example look like

    #     0     1     2     3     4    5
    my (undef,undef,undef,$mday,$mon,$year) = localtime();

Slice the Right-Hand Side

The unwanted values can also be eliminated by slicing the right-hand side of the assignment. This looks like

    #     3     4    5
    my ($mday,$mon,$year) = ( localtime() )[ 3 .. 5 ];

or, if you prefer,

    #     3     4    5
    my ($mday,$mon,$year) = ( localtime() )[ 3, 4, 5 ];

SUPPORT

Support is by the author. Please file bug reports at https://rt.cpan.org/Public/Dist/Display.html?Name=Perl-Critic-Policy-Variables-ProhibitUnusedVarsStricter, https://github.com/trwyant/perl-Perl-Critic-Policy-Variables-ProhibitUnusedVarsStricter/issues, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT

Copyright (C) 2012-2022 Thomas R. Wyant, III

LICENSE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.