http://qs321.pair.com?node_id=1176417


in reply to Searching for duplication in legacy code

My Devel::Examine::Subs can help with some of this. It uses PPI behind the scenes. It can gather all subs in a file, or a whole directory, then list all subs in all those files. It can even examine each sub and collect only ones that have lines containing specified search patterns, print out which lines each sub starts/ends, and also how many lines are in each sub.

Collect and display all subs in all files in the current working directory:

use warnings; use strict; use Devel::Examine::Subs; my $des = Devel::Examine::Subs->new(file => '.'); my $data = $des->all; for my $file (keys %$data){ print "$file\n"; for (@{ $data->{$file} }){ print "\t$_\n"; } }

Sample output:

lib/Test/BrewBuild/Git.pm new git link name clone pull lib/Test/BrewBuild/BrewCommands.pm new brew info installed using available install remove is_win _legacy_perls

Get all the subs in the same manner, but collect them as objects instead to get a lot more information on each one:

use warnings; use strict; use Devel::Examine::Subs; my $des = Devel::Examine::Subs->new(file => '.'); my $data = $des->all; for my $file (keys %$data){ print "$file\n"; my $subs = $des->objects(file => $file); for my $sub (@$subs){ print "\t" . $sub->name ."\n"; print "\t\t lines: " . $sub->line_count ."\n"; print "\t\t start: " . $sub->start ."\n"; print "\t\t end: " . $sub->end . "\n"; } }

Sample output:

lib/Test/BrewBuild/Dispatch.pm _fork lines: 111 start: 146 end: 256 new lines: 21 start: 21 end: 41 dispatch lines: 87 start: 42 end: 128 _config lines: 17 start: 129 end: 145 lib/Test/BrewBuild/Git.pm name lines: 6 start: 34 end: 39 git lines: 17 start: 12 end: 28

The main reason I wrote this software is so that I could introspect subs accurately, and then if necessary insert code in specific subs at either a line number or search term (yes, this distribution does that as well). You can even search for specific lines in each sub, and print out the line numbers those search patterns appear on.

Of course, using the above techniques, it would be trivial to filter out which files have duplicated subs, stash all the duplicate names (along with file name) then using the objects, compare the length of the subs to do a cursory check to see if they appear to be an exact copy/paste (if the number of lines are the same). The synopsis in the docs explain how to get the objects within a hash, so that the hash's key is the sub's name. This may make things easier.

update: I forgot to mention that each subroutine object also contains the full code for the sub in $sub->code. This should help tremendously in programmatically comparing a sub from one file to the dup sub in another file.