NAME

Perl::Critic::Policy::References::ProhibitUnnecessaryRefs - Use basic arrays and hashes.

DESCRIPTION

Perl arrays and hashes should be declared using the built-in basic data types, and functions should return basic arrays and hashes where possible. Each basic array or hash can be passed by reference after assignment. Scalar variables should only be used for scalar values (numbers/strings/versions), or for blessed objects.

$scalar   =foo();    # yes
$array[5] =foo();    # yes
$hash{key}=foo();    # yes
@array               # yes
%hash                # yes
$obj->method()       # yes
&$coderef(...);      # yes
*$globref{...}       # yes

$$aref[5]   =foo();  # no
$aref->[5]  =foo();  # no
@$arrayref           # no
$$href{key} =foo();  # no
$href->{key}=foo();  # no
%$hashref            # no

Violations occur for any declared, assigned, or auto-vivified variable, including declarations within conditional/loop statements.

Note that violations are tied to the scope of the variable's declaration and usage, so a clear object reference with method calls in one scope may be a violation in another scope where it's only used as e.g. a hash.

CONFIGURATION

Self

By default, the $self variable is exempt from violations. To enable violations for $self:

[References::ProhibitUnnecessaryRefs]
self = 1

VIOLATIONS

Violations to this policy occur when a scalar variable (of the form $var) has been assigned and the variable:

* is never cast to a non-array/hash, eg &$var; and

* is never used in a method call, eg $var->method(); and

* the only non-scalar usage of the variable within its scope is as an array reference or hash reference (but not both).

Violation location

Variable usage is associated with the point of assignment to that variable. Typically this occurs at declaration but, since variables may be reused, includes all individual assignments. Only scalar variables, $var, are considered, since any @array or %hash is already a plain data type. If a variable is never declared, its first usage (see below) will be considered its point of assignment.

Note that a variable assignment may have a location prior to a usage of that variable. Because assignment occurs right-to-left, RHS evaluation may refer to a previous version of the variable despite having a later location than the LHS assignment. For example, $x=f($x) uses a version of $x that exists when the expression is evaluated, but the LHS $x may have changed type.

Usage details

Every usage of scalar variables is considered, so a single variable may have multiple, different typed usages (such as in a class with override support). Usage types that are tracked include:

* any casting operator, x$var or x{$var}, for any possible x (such as *$var or %$var).

* any subscript, $$var{k}, $var-[i]>, or $var-()>, for example.

Usage inside quotes with interpolation is not currently considered; eg "foo $$x{key} bar" will not be detected.

Any detected usage is associated with the variable's violation location as described above. That is, usage is associated with a declared variable of that name currently in scope.

For variables that are not declared, the first usage will be considered the point of assignment, but this only applies to the usage types described here. For example, calling $x++ is never considered in determining type, so that statement by itself will never be the fallback point of assignment.

Usage of the form $x, @x, or %x, is never considered since those are associated with the plain variable x.

Scope mechanisms

This subsection contains technical details, but briefly: Reused declared variable names inside a block do not affect the usage information outside that block.

The scope is tracked for all blocks as a stack so usage can be associated with the variable currently in scope, if any. When the PPI document is walked, a step() may encounter a last/leaf/terminal node and need to exit through the parent(). Exit through a block pops the scope-stack and any associated variable scope. Moreover, since a step() may proceed through several parent() calls, the scope-stack may pop multiple times.

Note that the currently active scope can still have multiple assignment locations (potential violations) per variable.

Package statements don't matter because my variables outside a block are still file scoped, as in package P; my $x; package main;. Package statements using curly braces, either {package P;...} or package P {...} have a surrounding block scope that houses any my variables.

Assignment notes

During assignment, RHS evaluation must precede the actual assignment, so the LHS is not considered the new scope for those variables until after the RHS has been analyzed. In particular, the LHS is deferred (from an analysis perspective) until the overall expression completes.

Chained assignment operations are also supported, such as with @B=($x,$y)=(f(),g()) . Please note that PPI does not currently consider my @B=my ($x,$y)=(1,2) to be two separate variable statements, as it only reports the second my as a plain built-in. (This is likely a bug in PPI)

Analysis Overview

The document is fully analyzed during the preparation step, so that any critic violations are determined based entirely on the assignment location and (forward-looking) usages per analyzed variable. During initial analysis, it's necessary to identify and track:

* any pending statements, such as an assignment LHS

* the stack of nodes that establishes the current variable scope, either a PPI::Document or a Statement::Block

* for each $symbol, a stack of the key, which is the violation location for the symbol, and the scope, which is the location of the active scope node during symbol declaration

* and thus, a set of all active scopes, keyed by the locations of those scopes

* and finally, for each violation location and symbol (pair), the usage types associated with the symbol.

BUGS

Postfix dereference

Postfix dereferencing is not currently detected properly and will not cause a violation.

Refaliasing

The behavior of refaliasing is unknown (in this version of the policy). Ideally refaliasing would solve the "pass by reference and immediately unpack" violations, but a separate configuration may be added in the future to support that pattern. Note that individual unpacking can provide no critic statements.

Multitype scalars

Incorrectly regarded as a bug. Suppose a reference is returned into a scalar but the type may vary; a common pattern is:

if(is_arrayref($thing)) { ... @$thing }
if(is_hashref ($thing)) { ... %$thing }

Instead, this policy recommends returning proper "records" with appropriately-named identifiers with clearly defined shapes. For example:

my %R=get(...); # results
if   ($R{authors}) { @authors=@{$R{authors}} }
elsif($R{book})    { @authors=map {$$_{author}} values %{$R{book}} }

Mutation

Incorrectly regarded as a bug. Reference assignments are often shorthand for objects to be mutated:

my $H=$$self{thing};
$$H{key}=f('key', ...);

Typically such code is much larger, hence the inclination toward abbreviations, however most cases are actually functional blocks of code. Data goes in, something is computed, and the output data is reassigned:

$$self{thing}{key}=update('key', ...);

For example, a common pattern is a loop:

  my %things=%{ $$self{thing} }; # shallow copy, fast
  foreach my $k (...) {
		$$self{thing}{$k}=update($things{$k});
  }

As larger chunks of code evolve more helpers and data shaping functions, the original abbreviations will be found lacking. If there are actual shorthand reference variables, however, this policy recommends that they should be explicitly noted for mutation:

my $things=$$self{thing};  ## no critic (UnnecessaryRefs):  Variable is mutated