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

NAME

Module::Checkstyle::Check - Base class for checks

WRITING CHECK MODULES

Module::Checkstyle is extensible via a plug-in mechanism. This guide takes you through the basic steps in writing your own check.

To write a check you create a module that lives in the namespace Module::Checkstyle::Check:: and that extends Module::Checkstyle::Check. As an example we'll write a check that counts the length of a named subroutine.

To begin with we create a module named Module::Checkstyle::Check::SubLength either via h2xs or Module::Starter.

In the file Module/Checkstyle/Check/SubLength.pm we enter the following code:

    package Module::Checkstyle::Check::SubLength;

    use strict;
    use warnings;
    use Readonly;

    use Module::Checkstyle::Util qw(:args :problem);
    use base qw(Module::Checkstyle::Check);

Next we need to tell Module::Checkstyle what we want to recive events for. We do this by overriding the subroutine register which is called if the check is enabled in the configuration file.

    sub register {
        return ('enter PPI::Statement::Sub' => \&enter_subroutine);
    }

The method register must return a hash with event => handler pairs (actually it's a list that is returned). The event is defined by an optional operation, enter or leave, followed by the type of PPI::Element we want to respond to, which in this case is PPI::Statement::Sub.

The plug-in is then instansiated by calling its new method. This method should read the configuration directives into the object itself because there can exist mulitple instances of the check with different configurations. The new method is supplied an instance of Module::Checkstyle::Config. Lets define the configuration-directive max-length as a readonly-variable and add a constructor that reads it into the instance.

    Readonly my $MAX_LENGTH => 'max-length';

    sub new {
        my ($class, $config) = @_;
        $class = ref $class || $class;

        my $self = bless { config => $config }, $class;

        $self->{$MAX_LENGTH} = as_numeric($config->get_directive($MAX_LENGTH));
        
        return $self;
    }

The function as_numeric is provided by Module::Checkstyle::Util via the tag args.

Next we need to write our event handling code that is called when a named subroutine is found. Event-handlers retrieves three arguments: the instance of the plug-in, the PPI::Element and the path of the current file that is being processed.

    my enter_subroutine {
        my ($self, $subroutine, $file) = @_;

        my @problems;

        if ($self->{$MAX_LENGTH}) { # No need to run code if it's turned off
            my $block = $subroutine->block();
            if ($block) { # It's not a forward declaration
                my $line = $subroutine->location->[0]; # The line the declartion is on
                my $last_line = $block->last_element->location->[0]; # The line where } is at
                my $length = $last_line - $line;
                if ($length > $self->{$MAX_LENGTH}) {
                    my $name = $subroutine->name();
                    push @problems, make_problem($self->{config}->get_severity($MAX_LENGTH),
                                                 "Subroutine '$name' is too long ($length)",
                                                 $subroutine->location,
                                                 $file);
                }
            }
        }

        return @problems;
    }

The function make_problem is exported by Module::Checkstyle::Util in the tag problem.

To finish up our module we write the documentation, see Module::Checkstyle::Check::Package for an example, write the tests and make sure the module returns a true value.

METHODS

new ($config)

Default constructor that returns a hash-reference blessed to the subclass. The returned object will have the passed configuration object available under the key _config.

register

Abstract method that subclasses should override that provides the events it responds to.

config

Returns the config passed to new.