| Perl-Critic documentation | Contained in the Perl-Critic distribution. |
Perl::Critic::Policy::NamingConventions::Capitalization - Distinguish different program components by case.
This Policy is part of the core Perl::Critic distribution.
Conway recommends to distinguish different program components by case.
Normal subroutines, methods and variables are all in lower case.
my $foo; # ok
my $foo_bar; # ok
sub foo {} # ok
sub foo_bar {} # ok
my $Foo; # not ok
my $foo_Bar; # not ok
sub Foo {} # not ok
sub foo_Bar {} # not ok
Package and class names are capitalized.
package IO::Thing; # ok
package Web::FooBar # ok
package foo; # not ok
package foo::Bar; # not ok
Constants are in all-caps.
Readonly::Scalar my $FOO = 42; # ok
Readonly::Scalar my $foo = 42; # not ok
There are other opinions on the specifics, for example, in perlstyle (perlstyle). This policy can be configured to match almost any style that you can think of.
You can specify capitalization rules for the following things:
packages, subroutines, local_lexical_variables,
scoped_lexical_variables, file_lexical_variables,
global_variables, constants, and labels.
constants are things declared via constant or
Readonly.
use constant FOO => 193;
Readonly::Array my @BAR => qw< a b c >;
global_variables are anything declared using local, our, or
vars. file_lexical_variables are variables declared at the
file scope.
scoped_lexical_variables are variables declared inside bare blocks
that are outside of any subroutines or other control structures; these
are usually created to limit scope of variables to a given subset of
subroutines. E.g.
sub foo { ... }
{
my $thingy;
sub bar { ... $thingy ... }
sub baz { ... $thingy ... }
}
All other variable declarations are considered
local_lexical_variables.
Each of the packages, subroutines, local_lexical_variables,
scoped_lexical_variables, file_lexical_variables,
global_variables, constants, and labels options can be
specified as one of :single_case, :all_lower, :all_upper:,
:starts_with_lower, :starts_with_upper, or :no_restriction or
a regular expression; any value that does not start with a colon,
:, is considered to be a regular expression. The :single_case
tag means a name can be all lower case or all upper case. If a
regular expression is specified, it is surrounded by \A and \z.
packages defaults to :starts_with_upper. subroutines,
local_lexical_variables, scoped_lexical_variables,
file_lexical_variables, and global_variables default to
:single_case. And constants and labels default to
:all_upper.
There are corresponding package_exemptions,
subroutine_exemptions, local_lexical_variable_exemptions,
scoped_lexical_variable_exemptions,
file_lexical_variable_exemptions, global_variable_exemptions,
constant_exemptions, and label_exemptions options that are lists
of regular expressions to exempt from the corresponding capitalization
rule. These values also end up being surrounded by \A and \z.
package_exemptions defaults to main. global_variable_exemptions
defaults to
\$VERSION @ISA @EXPORT(?:_OK)? %EXPORT_TAGS \$AUTOLOAD %ENV %SIG \$TODO.
subroutine_exemptions defaults to
AUTOLOAD BUILD BUILDARGS CLEAR CLOSE DELETE DEMOLISH DESTROY EXISTS EXTEND FETCH FETCHSIZE FIRSTKEY GETC NEXTKEY POP PRINT PRINTF PUSH READ READLINE SCALAR SHIFT SPLICE STORE STORESIZE TIEARRAY TIEHANDLE TIEHASH TIESCALAR UNSHIFT UNTIE WRITE
which should cover all the standard Perl subroutines plus those from
Moose.
For example, if you want all local variables to be in all lower-case and global variables to start with "G_" and otherwise not contain underscores, but exempt any variable with a name that contains "THINGY", you could put the following in your .perlcriticrc:
[NamingConventions::Capitalization]
local_lexical_variables = :all_lower
global_variables = G_(?:(?!_)\w)+
global_variable_exemptions = .*THINGY.*
Handle use vars. Treat constant subroutines like constant
variables. Handle bareword file handles. There needs to be "schemes"
or ways of specifying "perlstyle" or "pbp". Differentiate lexical
Readonly constants in scopes.
This policy won't catch problems with the declaration of $y below:
for (my $x = 3, my $y = 5; $x < 57; $x += 3) {
...
}
Multiple people
Copyright (c) 2008-2011 Michael G Schwern. All rights reserved.
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.
| Perl-Critic documentation | Contained in the Perl-Critic distribution. |
############################################################################## # $URL: http://perlcritic.tigris.org/svn/perlcritic/trunk/distributions/Perl-Critic/lib/Perl/Critic/Policy/NamingConventions/Capitalization.pm $ # $Date: 2011-05-15 16:34:46 -0500 (Sun, 15 May 2011) $ # $Author: clonezone $ # $Revision: 4078 $ ############################################################################## package Perl::Critic::Policy::NamingConventions::Capitalization; use 5.006001; use strict; use warnings; use English qw< -no_match_vars >; use Readonly; use List::MoreUtils qw< any >; use Perl::Critic::Exception::AggregateConfiguration; use Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue; use Perl::Critic::Utils qw< :booleans :characters :severities hashify is_perl_global >; use Perl::Critic::Utils::Perl qw< symbol_without_sigil >; use Perl::Critic::Utils::PPI qw< is_in_subroutine >; use PPIx::Utilities::Statement qw< get_constant_name_elements_from_declaring_statement >; use base 'Perl::Critic::Policy'; our $VERSION = '1.116'; #----------------------------------------------------------------------------- # Don't worry about leading digits-- let perl/PPI do that. Readonly::Scalar my $ALL_ONE_CASE_REGEX => qr< \A [@%\$]? (?: [[:lower:]_\d]+ | [[:upper:]_\d]+ ) \z >xms; Readonly::Scalar my $ALL_LOWER_REGEX => qr< \A [[:lower:]_\d]+ \z >xms; Readonly::Scalar my $ALL_UPPER_REGEX => qr< \A [[:upper:]_\d]+ \z >xms; Readonly::Scalar my $STARTS_WITH_LOWER_REGEX => qr< \A _* [[:lower:]\d] >xms; Readonly::Scalar my $STARTS_WITH_UPPER_REGEX => qr< \A _* [[:upper:]\d] >xms; Readonly::Scalar my $NO_RESTRICTION_REGEX => qr< . >xms; Readonly::Hash my %CAPITALIZATION_SCHEME_TAGS => ( ':single_case' => { regex => $ALL_ONE_CASE_REGEX, regex_violation => 'is not all lower case or all upper case', }, ':all_lower' => { regex => $ALL_LOWER_REGEX, regex_violation => 'is not all lower case', }, ':all_upper' => { regex => $ALL_UPPER_REGEX, regex_violation => 'is not all upper case', }, ':starts_with_lower' => { regex => $STARTS_WITH_LOWER_REGEX, regex_violation => 'does not start with a lower case letter', }, ':starts_with_upper' => { regex => $STARTS_WITH_UPPER_REGEX, regex_violation => 'does not start with a upper case letter', }, ':no_restriction' => { regex => $NO_RESTRICTION_REGEX, regex_violation => 'there is a bug in Perl::Critic if you are reading this', }, ); Readonly::Scalar my $PACKAGE_REGEX => qr/ :: | ' /xms; Readonly::Hash my %NAME_FOR_TYPE => ( package => 'Package', subroutine => 'Subroutine', local_lexical_variable => 'Local lexical variable', scoped_lexical_variable => 'Scoped lexical variable', file_lexical_variable => 'File lexical variable', global_variable => 'Global variable', constant => 'Constant', label => 'Label', ); Readonly::Hash my %IS_COMMA => hashify( $COMMA, $FATCOMMA ); Readonly::Scalar my $EXPL => [ 45, 46 ]; #----------------------------------------------------------------------------- # Can't handle named parameters yet. sub supported_parameters { return ( { name => 'packages', description => 'How package name components should be capitalized. Valid values are :single_case, :all_lower, :all_upper:, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':starts_with_upper', behavior => 'string', }, { name => 'package_exemptions', description => 'Package names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => 'main', behavior => 'string list', }, { name => 'subroutines', description => 'How subroutine names should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':single_case', # Matches ProhibitMixedCaseSubs behavior => 'string', }, { name => 'subroutine_exemptions', description => 'Subroutine names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => join ( $SPACE, qw< AUTOLOAD BUILD BUILDARGS CLEAR CLOSE DELETE DEMOLISH DESTROY EXISTS EXTEND FETCH FETCHSIZE FIRSTKEY GETC NEXTKEY POP PRINT PRINTF PUSH READ READLINE SCALAR SHIFT SPLICE STORE STORESIZE TIEARRAY TIEHANDLE TIEHASH TIESCALAR UNSHIFT UNTIE WRITE >, ), behavior => 'string list', }, { name => 'local_lexical_variables', description => 'How local lexical variables names should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':single_case', # Matches ProhibitMixedCaseVars behavior => 'string', }, { name => 'local_lexical_variable_exemptions', description => 'Local lexical variable names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => $EMPTY, behavior => 'string list', }, { name => 'scoped_lexical_variables', description => 'How lexical variables that are scoped to a subset of subroutines, should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':single_case', # Matches ProhibitMixedCaseVars behavior => 'string', }, { name => 'scoped_lexical_variable_exemptions', description => 'Names for variables in anonymous blocks that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => $EMPTY, behavior => 'string list', }, { name => 'file_lexical_variables', description => 'How lexical variables at the file level should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':single_case', # Matches ProhibitMixedCaseVars behavior => 'string', }, { name => 'file_lexical_variable_exemptions', description => 'File-scope lexical variable names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => $EMPTY, behavior => 'string list', }, { name => 'global_variables', description => 'How global (package) variables should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':single_case', # Matches ProhibitMixedCaseVars behavior => 'string', }, { name => 'global_variable_exemptions', description => 'Global variable names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => '\$VERSION @ISA @EXPORT(?:_OK)? %EXPORT_TAGS \$AUTOLOAD %ENV %SIG \$TODO', ## no critic (RequireInterpolation) behavior => 'string list', }, { name => 'constants', description => 'How constant names should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':all_upper', behavior => 'string', }, { name => 'constant_exemptions', description => 'Constant names that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => $EMPTY, behavior => 'string list', }, { name => 'labels', description => 'How labels should be capitalized. Valid values are :single_case, :all_lower, :all_upper, :starts_with_lower, :starts_with_upper, :no_restriction, or a regex.', default_string => ':all_upper', behavior => 'string', }, { name => 'label_exemptions', description => 'Labels that are exempt from capitalization rules. The values here are regexes that will be surrounded by \A and \z.', default_string => $EMPTY, behavior => 'string list', }, ); } sub default_severity { return $SEVERITY_LOWEST } sub default_themes { return qw< core pbp cosmetic > } sub applies_to { return qw< PPI::Statement PPI::Token::Label > } #----------------------------------------------------------------------------- sub initialize_if_enabled { my ($self, $config) = @_; my $configuration_exceptions = Perl::Critic::Exception::AggregateConfiguration->new(); KIND: foreach my $kind_of_name ( qw< package subroutine local_lexical_variable scoped_lexical_variable file_lexical_variable global_variable constant label > ) { my ($capitalization_regex, $message) = $self->_derive_capitalization_test_regex_and_message( $kind_of_name, $configuration_exceptions, ); my $exemption_regexes = $self->_derive_capitalization_exemption_test_regexes( $kind_of_name, $configuration_exceptions, ); # Keep going, despite problems, so that all problems can be reported # at one go, rather than the user fixing one problem, receiving a new # error, etc.. next KIND if $configuration_exceptions->has_exceptions(); $self->{"_${kind_of_name}_test"} = sub { my ($name) = @_; return if _name_is_exempt($name, $exemption_regexes); return $message if $name !~ m/$capitalization_regex/xms; return; } } if ( $configuration_exceptions->has_exceptions() ) { $configuration_exceptions->throw(); } return $TRUE; } sub _derive_capitalization_test_regex_and_message { my ($self, $kind_of_name, $configuration_exceptions) = @_; my $capitalization_option = "${kind_of_name}s"; my $capitalization = $self->{"_$capitalization_option"}; if ( my $tag_properties = $CAPITALIZATION_SCHEME_TAGS{$capitalization} ) { return @{$tag_properties}{ qw< regex regex_violation > }; } elsif ($capitalization =~ m< \A : >xms) { $configuration_exceptions->add_exception( Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue->new( policy => $self, option_name => $capitalization_option, option_value => $capitalization, message_suffix => 'is not a known capitalization scheme tag. Valid tags are: ' . (join q<, >, sort keys %CAPITALIZATION_SCHEME_TAGS) . $PERIOD, ) ); return; } my $regex; eval { $regex = qr< \A $capitalization \z >xms; } or do { $configuration_exceptions->add_exception( Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue->new( policy => $self, option_name => $capitalization_option, option_value => $capitalization, message_suffix => "is not a valid regular expression: $EVAL_ERROR", ) ); return; }; return $regex, qq<does not match "\\A$capitalization\\z".>; } sub _derive_capitalization_exemption_test_regexes { my ($self, $kind_of_name, $configuration_exceptions) = @_; my $exemptions_option = "${kind_of_name}_exemptions"; my $exemptions = $self->{"_$exemptions_option"}; my @regexes; PATTERN: foreach my $pattern ( keys %{$exemptions} ) { my $regex; eval { $regex = qr< \A $pattern \z >xms; } or do { $configuration_exceptions->add_exception( Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue->new( policy => $self, option_name => $exemptions_option, option_value => $pattern, message_suffix => "is not a valid regular expression: $EVAL_ERROR", ) ); next PATTERN; }; push @regexes, $regex; } return \@regexes; } sub _name_is_exempt { my ($name, $exemption_regexes) = @_; foreach my $regex ( @{$exemption_regexes} ) { return $TRUE if $name =~ m/$regex/xms; } return $FALSE; } #----------------------------------------------------------------------------- sub violates { my ( $self, $elem, undef ) = @_; # Want given. Want 5.10. Gimme gimme gimme. :] if ( $elem->isa('PPI::Statement::Variable') ) { return $self->_variable_capitalization($elem); } if ( $elem->isa('PPI::Statement::Sub') ) { return $self->_subroutine_capitalization($elem); } if ( my @names = get_constant_name_elements_from_declaring_statement($elem) ) { return ( grep { $_ } map { $self->_constant_capitalization( $elem, $_ ) } @names ) } if ( $elem->isa('PPI::Statement::Package') ) { return $self->_package_capitalization($elem); } if ( $elem->isa('PPI::Statement::Compound') and $elem->type() eq 'foreach' ) { return $self->_foreach_variable_capitalization($elem); } if ( $elem->isa('PPI::Token::Label') ) { return $self->_label_capitalization($elem); } return; } sub _variable_capitalization { my ($self, $elem) = @_; my @violations; NAME: for my $name ( map { $_->symbol() } $elem->symbols() ) { if ($elem->type() eq 'local') { # Fully qualified names are exempt because we can't be responsible # for other people's sybols. next NAME if $name =~ m/$PACKAGE_REGEX/xms; next NAME if is_perl_global($name); push @violations, $self->_check_capitalization( symbol_without_sigil($name), $name, 'global_variable', $elem, ); } elsif ($elem->type() eq 'our') { push @violations, $self->_check_capitalization( symbol_without_sigil($name), $name, 'global_variable', $elem, ); } else { # Got my or state my $parent = $elem->parent(); if ( not $parent or $parent->isa('PPI::Document') ) { push @violations, $self->_check_capitalization( symbol_without_sigil($name), $name, 'file_lexical_variable', $elem, ); } else { if ( _is_directly_in_scope_block($elem) ) { push @violations, $self->_check_capitalization( symbol_without_sigil($name), $name, 'scoped_lexical_variable', $elem, ); } else { push @violations, $self->_check_capitalization( symbol_without_sigil($name), $name, 'local_lexical_variable', $elem, ); } } } } return @violations; } sub _subroutine_capitalization { my ($self, $elem) = @_; # These names are fixed and you've got no choice what to call them. return if $elem->isa('PPI::Statement::Scheduled'); my $name = $elem->name(); $name =~ s{ .* :: }{}smx; # Allow for "sub Some::Package::foo {}" return $self->_check_capitalization($name, $name, 'subroutine', $elem); } sub _constant_capitalization { my ($self, $elem, $name) = @_; return $self->_check_capitalization( symbol_without_sigil($name), $name, 'constant', $elem, ); } sub _package_capitalization { my ($self, $elem) = @_; my $namespace = $elem->namespace(); my @components = split m/::/xms, $namespace; foreach my $component (@components) { my $violation = $self->_check_capitalization( $component, $namespace, 'package', $elem, ); return $violation if $violation; } return; } sub _foreach_variable_capitalization { my ($self, $elem) = @_; my $type; my $symbol; my $second_element = $elem->schild(1); return if not $second_element; if ($second_element->isa('PPI::Token::Word')) { $type = $second_element->content(); $symbol = $second_element->snext_sibling(); } else { $type = 'my'; $symbol = $second_element; } return if not $symbol; return if not $symbol->isa('PPI::Token::Symbol'); my $name = $symbol->symbol(); if ($type eq 'local') { # Fully qualified names are exempt because we can't be responsible # for other people's sybols. return if $name =~ m/$PACKAGE_REGEX/xms; return if is_perl_global($name); return $self->_check_capitalization( symbol_without_sigil($name), $name, 'global_variable', $elem, ); } elsif ($type eq 'our') { return $self->_check_capitalization( symbol_without_sigil($name), $name, 'global_variable', $elem, ); } # Got my or state: treat as local lexical variable return $self->_check_capitalization( symbol_without_sigil($name), $name, 'local_lexical_variable', $elem, ); } sub _label_capitalization { my ($self, $elem, $name) = @_; return if _is_not_real_label($elem); ( my $label = $elem->content() ) =~ s< \s* : \z ><>xms; return $self->_check_capitalization($label, $label, 'label', $elem); } sub _check_capitalization { my ($self, $to_match, $full_name, $name_type, $elem) = @_; my $test = $self->{"_${name_type}_test"}; if ( my $message = $test->($to_match) ) { return $self->violation( qq<$NAME_FOR_TYPE{$name_type} "$full_name" $message>, $EXPL, $elem, ); } return; } # { my $x } parses as # PPI::Document # PPI::Statement::Compound # PPI::Structure::Block { ... } # PPI::Statement::Variable # PPI::Token::Word 'my' # PPI::Token::Symbol '$x' # PPI::Token::Structure ';' # # Also, type() on the PPI::Statement::Compound returns "continue". *sigh* # # The parameter is expected to be the PPI::Statement::Variable. sub _is_directly_in_scope_block { my ($elem) = @_; return if is_in_subroutine($elem); my $parent = $elem->parent(); return if not $parent->isa('PPI::Structure::Block'); my $grand_parent = $parent->parent(); return $TRUE if not $grand_parent; return $TRUE if $grand_parent->isa('PPI::Document'); return if not $grand_parent->isa('PPI::Statement::Compound'); my $type = $grand_parent->type(); return if not $type; return if $type ne 'continue'; my $great_grand_parent = $grand_parent->parent(); return if $great_grand_parent and not $great_grand_parent->isa('PPI::Document'); # Make sure we aren't really in a continue block. my $prior_to_grand_parent = $grand_parent->sprevious_sibling(); return $TRUE if not $prior_to_grand_parent; return $TRUE if not $prior_to_grand_parent->isa('PPI::Token::Word'); return $prior_to_grand_parent->content() ne 'continue'; } sub _is_not_real_label { my $elem = shift; # PPI misparses part of a ternary expression as a label # when the token to the left of the ":" is a bareword. # See http://rt.cpan.org/Ticket/Display.html?id=41170 # For example... # # $foo = $condition ? undef : 1; # # PPI thinks that "undef" is a label. To workaround this, # I'm going to check that whatever PPI thinks is the label, # actually is the first token in the statement. I believe # this should be true for all real labels. my $stmnt = $elem->statement() || return; my $first_child = $stmnt->schild(0) || return; return $first_child ne $elem; } 1; __END__ #-----------------------------------------------------------------------------
# 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 :