Abstract
You must be probably aware of static code analysis. Yes, it’s a technique that is extensively used in software development to find out programming errors that a developer might have committed. It is termed static because it is done on the source code without actually executing it. Want to analyze your Perl program for possible programming errors? B::Lint comes to the rescue!
The purpose of this article is to make the readers aware about the B::Lint module that can be used for performing static code analysis of Perl code. The author explains the checks provided by Lint and substantiates it with relevant coding examples for better understanding of the subject. The article encourages Perl developers to use this module so that they can tap on the benefits provided by it.
Perl Lint
According to Wikipedia, “In computer programming, Lint was the name originally given to a particular program that flagged some suspicious and non-portable constructs (likely to be bugs) in C language source code.” In the software engineering world, Lint is generally referred to the tools that perform static code analysis for various programming languages.
Perl B::Lint is an easy to use and a continually improving module developed by Malcolm Beattie, mbeattie@sable.ox.ac.uk.
Lint is enabled on the command line by providing available options. If we have a Perl script named ‘Lint.pl’ that needs to be analyzed for possible programming errors, then the command ‘perl.exe –MO=Lint[,options] Lint.pl’ would enable Linting on the source code. ‘options’ are essentially the checks that Lint provides to its users. It is worthwhile to note that the options are separated by commas and not by whitespaces.
Lint checks with coding examples
Magic Diamond: A diamond operator is used in a Perl script for either reading from @ARGV (Perl special variable that contains the command line arguments) or from the standard streams like STDIN (Standard Input Stream) or from File handles (with which files are opened for reading or writing). When diamond operator is used for reading from @ARGV or used without arguments, Lint throws an alert message suggesting that a diamond operator is used in the source code. Let’s have a look at the sample program and the warning issued by Lint, to make the concept clear.
Lint.pl
[sourcecode language=”perl”]
#C:\\perl\\bin\\perl
print "Enter filename: ";
chop(my $file = <>);
system(“cat $file”);
[/sourcecode]
Enabling Lint
perl -MO=Lint,magic-diamond Lint.pl
Warning
Use of <> at Lint.pl line 3
Lint.pl syntax OK
The objective of the above code is to take a filename as an input from the user and print the contents of that file. In lint.pl, we have used a blank diamond operator(<>), which means that the Command Line Arguments (@ARGV) would be checked first for inputs and if @ARGV is not defined, Perl would wait for inputs from Standard Input Stream(STDIN). Security is always a concern when the inputs come from outside a program. So, when the diamond operator is used for reading from @ARGV or used without arguments, Lint throws a warning message. This warning alerts the coder. An input to the above Perl script could be ‘/etc/passwd’, which would result in the script showing the contents of password file. This could be used for achieving malicious intentions. Programmers thus can ensure that sufficient checks are enabled in their Perl code to avoid such situations.
Context: This option issues a warning when an array is used in scalar context. The length (or the size) of the array is used in calculation instead of using the elements in the array when an array is used in a numerical context. For example, take a look at the below code snippet with the warning issued by Lint.
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
sub context {
my $arrayref = shift @_;
print @$arrayref;
print @$arrayref – 2;
}
my $number = 5;
my @arr = ();
push(@arr, $number);
context(\@arr);
[/sourcecode]
Enabling Lint
perl -MO=Lint,context Lint.pl
Warning
Implicit scalar context for array in numeric lt (<) at Lint.pl line 6
Lint.pl syntax OK
In the line 6 of the Perl code, array ‘@$arrayref’ is used in the scalar context, which means it takes on value ‘1’ as there is only one element present in the array. Lint issues a warning here as the array is being used in a scalar context (and not as an array). Sometimes, Perl developers do commit a mistake of assuming that the statement ‘print @$arrayref – 2’ would result in printing ‘3’ as @$arrayref has a single element ‘5’. Lint helps the programmers by catching such erroneous assumptions.
Implicit-read and Implicit-write: Lint uses this option to warn the programmer when any of the Perl’s special variable (like $_, $@) is read from or written into. Check out this example.
Lint.pl
[sourcecode language=”perl”]
open(FH, "C:\\drivey.log");
while(<FH>) {
s/Parsing/Nothing/;
print "Matched" if m/Parsing/; }
close FH;
[/sourcecode]
Enabling Lint
perl -MO=Lint,implicit-read,implicit-write Lint.pl
Warning
Implicit substitution on $_ at Lint.pl line 5
Implicit match on $_ at Lint.pl line 6
Lint.pl syntax OK
In the above example, implicit substitution and matching of $_ occurs when we substitute pattern ‘Parsing with Nothing’ (s/Parsing/Nothing/) and match for a pattern ‘Parsing’ (print “Matched” if m/Parsing/). A good coding practice advocates usage of explicit variable that can be used to loop over data structures or file contents. This makes the code readable and the programmer is explicitly aware of the things being achieved with that variable. Lint warns the coder on the implicit usage of $_ so that it’s not used inadvertently.
Bare-subs: This option warns the programmer for using a bare word in the program even when the bare word is the name of the subroutine that is defined in the program. Here’s an example:
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
sub add { 2+2;
}
$hash{add};
[/sourcecode]
Enabling Lint
perl -MO=Lint,bare-subs Lint.pl
Warning
Bare sub name ‘add’ interpreted as string at Lint.pl line 5
Lint.pl syntax OK
In the above code snippet, we are using the bare word ‘add’ as a key for a hash. This is caught by Lint and is reported as ‘add is interpreted as string’. This avoids the programmer from using ‘add ‘ as a subroutine name and also using it as a key name in a hash. Thus Lint prevents coding errors that cause issues during program execution and are difficult to debug.
Dollar-underscore: It checks for all those statements where Perl special variable $_is used implicitly in the print statement or is explicitly used in the Perl code.
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
open(FH, "C:\\drivey.log");
@contents = <FH>;
for (@contents) {
print ;
}
close FH;
[/sourcecode]
Command
perl -MO=Lint,dollar-underscore Lint.pl
Warning
Use of $_ at Lint.pl line 6
Lint.pl syntax OK
If we have a look at the above code, Perl special variable $_ is used implicitly in the print statement. Lint warns the programmer on this so that it’s not used accidentally.
Private_names: Subroutine or variable names that are prefixed with “_” symbol are private subroutines or private variables of a program. Lint warns the coder if private variables or subroutines of a different package are imported to the current package. Consider we have a module ‘private.pm’ and a Perl script ‘lint.pl’ as below.
Private.pm
[sourcecode language=”perl”]
package Private;
use strict;
use Exporter;
our @ISA = qw(Exporter);
sub _sum {
$_[0] + $_[1]; }
1;
[/sourcecode]
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
use Private;
print &Private::_sum(2,3);
[/sourcecode]
Enabling Lint
perl –MO=Lint,private_names Lint.pl
Warning
Illegal reference to private name ‘_sum’ at Lint.pl line 3
Lint.pl syntax OK
In the above code snippet, we have created a module called Private.pm. We are importing the module in our script Lint.pl and calling the private subroutine _sum(). Lint warns on calling _sum() and suggests that the script is trying to access a private name from a different module which is not a good programming practice and hence should be avoided.
Undefined-subs: As the name suggests, this check warns the programmers about the undefined subroutines that are used by the programmer in the code.
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
sub adder {
$_[0] + $_[1]; }
print adder(2,3);
print add();
[/sourcecode]
Enabling Lint
perl -MO=Lint,undefined-subs Lint.pl
Warning
Nonexistent subroutine ‘add’ called at Lint.pl line 6
Lint.pl syntax OK
In the above code, we have a subroutine adder() which is defined and then called to add two integers 2 and 3. Along with that we have called a subroutine add(). Add() is undefined but still called in the program. Lint warns with an error stating that the subroutine add() doesn’t exist. In large programs, it often happens that a programmer defines a subroutine at the beginning of a program and uses it later with another name thinking that as the actual name of subroutine. Such mistakes can be avoided if Lint is enabled.
Regexp-variables: This option warns for the usage of Perl special variables $`, $&, $` in the source code. The chief reason why the usage of these variables is alerted is because it slows down the code execution. A Perl source code using $` is shown below.
Lint.pl
[sourcecode language=”perl”]
#!C:\\Perl\\bin\\perl
my $text = "The Perl rocks";
if($text =~ m/(Perl)/i) {
print $`; }<span style="text-decoration: underline;">
</span>[/sourcecode]
Enabling Lint
perl –MO=Lint,regexp-variables Lint.pl
Warning
Use of regexp variable $` at Lint.pl line 3
Lint.pl syntax OK
In this case, we have a string ‘The Perl rocks’ that is stored in a scalar variable $text. We then match the string with the pattern ‘Perl’ and print the pre-match string of the pattern matched. As we are aware, $` slows down the program execution, Lint warns on its usage in the lint.pl. Similarly, Lint warns for usage of $& (actual pattern match) and $’ (post-match) in the Perl code.
All: Want to enable all the Lint checks on a piece of Perl Code? Use the option all! It is nothing but turning on all the Lint warnings for performing the static code analysis instead of chosen Lint options. Typically programmers tend to use this option as all Lint checks get enabled for the complete program in one go.
Enabling Lint
perl –MO=Lint,all Lint.pl
No-<option>: I am now aware of the option used for enabling all the Lint checks. But how do I disable some of the checks if I need to? The answer to this is that Lint provides its users with an opportunity of disabling one or more of its checks with no-<option> check. An example for the same is listed below.
Enabling Lint
perl –MO=Lint,all,no-context Lint.pl
Carefully note the use of two options at a time. With this command, the programmer has asked Lint to enable all the checks except for a check that pertains to ‘context’ check. Similarly more checks can be disabled by using ‘no-<option>’ on the command line.
None: As the name suggests, this disables all the link checks (or options) that can be applied on Perl source code. Basically, it disables Lint and static analysis of code no more takes place.
Enabling Lint
perl –MO=Lint,none Lint.pl
Conclusion
In this article, we have had a look on Lint and the checks that it provides with suitable programming examples. The author would suggest the readers to refer the article again and get their hands dirty on B::Lint as soon as possible so that they can benefit from it.
References
1. http://perldoc.perl.org/B/Lint.html – Perl 5 Version 1.2.2 documentation.
Site maintained by Jon Allen (JJ), Documentation maintained by the Perl 5 Porters.
Date referred: 25 October 2010.
2. http://en.wikipedia.org/wiki/Lint_(software) – the free encyclopedia.
Date referred: 25 October 2010.
One thought on “Lint in Perl”