rename_rev.pl: review script for whitespace changes

From: Dan Carpenter
Date: Fri Jun 26 2015 - 09:15:49 EST


I've sent my review script out a few times before but we have some new
reviewers in staging who maybe haven't tried them.

rename_rev.pl strips out whitespace changes. We recently had someone
send a re-indent patch that deleted a line of code by mistake. The diff
looked like:

18 files changed, 906 insertions(+), 927 deletions(-)

It would be difficult to spot the bug manually but when you cat it
through rename_rev.pl then it stands out immediately.

If the patch changes // comments to /* */ then `rename_rev.pl -nc`
strips out most of the comments.

If the patch re-indents macros then the -ns removes slashes from the
ends of lines.

Sometimes people pull out some code into a separate function. The -pull
option is supposed to help for that. It sometimes does...

The other thing that we see a lot in staging is when people change curly
braces around. The -nb option removes curly brace changes.

Another thing is we had a change which did this:

-#define HOST_IF_MSG_SCAN ((u16)0)
-(40 lines of similar code)
+#define HOST_IF_MSG_SCAN 0
+(40 lines of similar code)

I used rename_rev.pl -e 's/\(\(u16\)(.*)\)/$1/' to make sure nothing
else changed.

Or if you are making hundreds of functions "static", then I just remove
all the statics by doing rename_rev.pl -ea 's/static//'. The -ea option
stands for Execute on All.

Oh. And I am also going to include my move_rev.pl, script. That is for
if we move functions from one file to another.

cat patch | move_rev.pl | rename_rev.pl

The rename_rev.pl script is sort of crappy, but often it removes a lot
of stuff. It doesn't have to be perfect to be better, I guess.

What I wish is that there were an -auto option which would find which
variables were renamed. Oh! Oh! I have left out the most important
feature. Say you are renaming variables from SomeName to some_name then
cat patch | rename_rev.pl SomeName some_name TwoName two_name Foo foo

regards,
dan carpenter
#!/usr/bin/perl

# This is a tool to help review variable rename patches. The goal is
# to strip out the automatic sed renames and the white space changes
# and leaves the interesting code changes.
#
# Example 1: A patch renames openInfo to open_info:
# cat diff | rename_review.pl openInfo open_info
#
# Example 2: A patch swaps the first two arguments to some_func():
# cat diff | rename_review.pl \
# -e 's/some_func\((.*?),(.*?),/some_func\($2, $1,/'
#
# Example 3: A patch removes the xkcd_ prefix from some but not all the
# variables. Instead of trying to figure out which variables were renamed
# just remove the prefix from them all:
# cat diff | rename_review.pl -ea 's/xkcd_//g'
#
# Example 4: A patch renames 20 CamelCase variables. To review this let's
# just ignore all case changes and all '_' chars.
# cat diff | rename_review -ea 'tr/[A-Z]/[a-z]/' -ea 's/_//g'
#
# The other arguments are:
# -nc removes comments
# -ns removes '\' chars if they are at the end of the line.

use strict;
use File::Temp qw/ :mktemp /;

sub usage() {
print "usage: cat diff | $0 old new old new old new...\n";
print " or: cat diff | $0 -e 's/old/new/g'\n";
print " -e : execute on old lines\n";
print " -ea: execute on all lines\n";
print " -nc: no comments\n";
print " -nb: no unneeded braces\n";
print " -ns: no slashes at the end of a line\n";
print " -pull: for function pull. deletes context.\n";
exit(1);
}
my @subs;
my @cmds;
my $strip_comments;
my $strip_braces;
my $strip_slashes;
my $pull_context;

sub filter($) {
my $_ = shift();
my $old = 0;
if ($_ =~ /^-/) {
$old = 1;
}
# remove the first char
s/^[ +-]//;
if ($strip_comments) {
s/\/\*.*?\*\///g;
s/\/\/.*//;
}
foreach my $cmd (@cmds) {
if ($old || $cmd->[0] =~ /^-ea$/) {
eval $cmd->[1];
}
}
foreach my $sub (@subs) {
if ($old) {
s/$sub->[0]/$sub->[1]/g;
}
}

# remove the newline so we can move curly braces here if we want.
s/\n//;
return $_;
}

while (my $param1 = shift()) {
if ($param1 =~ /^-nc$/) {
$strip_comments = 1;
next;
}
if ($param1 =~ /^-nb$/) {
$strip_braces = 1;
next;
}
if ($param1 =~ /^-ns$/) {
$strip_slashes = 1;
next;
}
if ($param1 =~ /^-pull$/) {
$pull_context = 1;
next;
}
my $param2 = shift();
if ($param2 =~ /^$/) {
usage();
}
if ($param1 =~ /^-e(a|)$/) {
push @cmds, [$param1, $param2];
next;
}
push @subs, [$param1, $param2];
}

my ($oldfh, $oldfile) = mkstemp("/tmp/oldXXXXX");
my ($newfh, $newfile) = mkstemp("/tmp/newXXXXX");

my $inside = 0;
my $output;

#recreate an old file and a new file
while (<>) {
if ($pull_context && !($_ =~ /^[+-@]/)) {
next;
}

if ($_ =~ /^(---|\+\+\+)/) {
next;
}

if ($_ =~ /^@/) {
$inside = 1;
}
if ($inside && !(($_ =~ /^[- @+]/) || ($_ =~ /^$/))) {
$inside = 0;
}
if (!$inside) {
next;
}

$output = filter($_);

if ($strip_braces && $_ =~ /^(\+|-)\W+{/) {
$output =~ s/^[\t ]+(.*)/ $1/;
} else {
$output = "\n" . $output;
}

if ($_ =~ /^-/) {
print $oldfh $output;
next;
}
if ($_ =~ /^\+/) {
print $newfh $output;
next;
}
print $oldfh $output;
print $newfh $output;

}
print $oldfh "\n";
print $newfh "\n";
# git diff puts a -- and version at the end of the diff. put the -- into the
# new file as well so it's ignored
if ($output =~ /\n-/) {
print $newfh "-\n";
}

my $hunk;
my $old_txt;
my $new_txt;

open diff, "diff -uw $oldfile $newfile |";
while (<diff>) {
if ($_ =~ /^(---|\+\+\+)/) {
next;
}

if ($_ =~ /^@/) {

if ($strip_comments) {
$old_txt =~ s/\/\*.*?\*\///g;
$new_txt =~ s/\/\*.*?\*\///g;
}
if ($strip_braces) {
$old_txt =~ s/{([^;{]*?);}/$1;/g;
$new_txt =~ s/{([^;{]*?);}/$1;/g;
# this is a hack because i don't know how to replace nested
# unneeded curly braces.
$old_txt =~ s/{([^;{]*?);}/$1;/g;
$new_txt =~ s/{([^;{]*?);}/$1;/g;
}

if ($old_txt ne $new_txt) {
print $hunk;
print $_;
}
$hunk = "";
$old_txt = "";
$new_txt = "";
next;
}

$hunk = $hunk . $_;

if ($strip_slashes) {
s/\\$//;
}

if ($_ =~ /^-/) {
s/-//;
s/[ \t\n]//g;
$old_txt = $old_txt . $_;
next;
}
if ($_ =~ /^\+/) {
s/\+//;
s/[ \t\n]//g;
$new_txt = $new_txt . $_;
next;
}
if ($_ =~ /^ /) {
s/^ //;
s/[ \t\n]//g;
$old_txt = $old_txt . $_;
$new_txt = $new_txt . $_;
}
}

if ($old_txt ne $new_txt) {
if ($strip_comments) {
$old_txt =~ s/\/\*.*?\*\///g;
$new_txt =~ s/\/\*.*?\*\///g;
}
if ($strip_braces) {
$old_txt =~ s/{([^;{]*?);}/$1;/g;
$new_txt =~ s/{([^;{]*?);}/$1;/g;
$old_txt =~ s/{([^;{]*?);}/$1;/g;
$new_txt =~ s/{([^;{]*?);}/$1;/g;
}

print $hunk;
}

unlink($oldfile);
unlink($newfile);

print "\ndone.\n";
#!/usr/bin/perl

use strict;
use File::Temp qw/ tempdir /;
use File::Path qw/ rmtree /;
use File::Compare;

sub filter($) {
my $_ = shift();

# remove the first char
s/^[ +-]//;
return $_;
}

sub break_out_blocks($)
{
my $dir = shift();
my $block_nr = 0;

open FILE, "<", "$dir/full";
open OUT, ">", "$dir/$block_nr";

while (<FILE>) {

if ($block_nr && $_ =~ /^}/) {
print OUT $_;
close(OUT);
$block_nr++;
open OUT, ">", "$dir/$block_nr";
next;
}

if ($_ =~ /^{/ || ($_ =~ /^[a-zA-Z]/ && $_ =~ / {$/)) {
close(OUT);
$block_nr++;
open OUT, ">", "$dir/$block_nr";
}

print OUT $_;
}
close(OUT);
}

sub files_same($$)
{
my $one = shift();
my $two = shift();
my $size_one = (stat($one))[7];
my $size_two = (stat($two))[7];

if ($size_one != $size_two) {
return 0;
}
return compare($one, $two) == 0;
}

sub is_block($)
{
my $file = shift();
open LINE, "<", "$file";
my $line = <LINE>;

if ($line =~ /^{/ || ($line =~ /^[a-zA-Z]/ && $line =~ / {$/)) {
return 1;
}
return 0;
}

sub replace_exact_blocks($$) {
my $olddir = shift();
my $newdir = shift();

my $i = -1;
while (1) {
$i++;
if (! -e "$olddir/$i") {
last;
}

if (!is_block("$olddir/$i")) {
next;
}

my $j = -1;
while (1) {
$j++;
if (! -e "$newdir/$j") {
last;
}

if (files_same("$olddir/$i", "$newdir/$j")) {

open OUT, ">", "$olddir/$i";
print OUT "- MOVED {$i}\n";
close OUT;

open OUT, ">", "$newdir/$j";
print OUT "+ MOVED {$j}\n";
close OUT;
last;
}

}
}
}

sub merge_blocks($) {
my $dir = shift();

open MERGED, ">", "$dir/merged";
my $i = -1;
while (1) {
$i++;
if (! -e "$dir/$i") {
last;
}

open FILE, "<", "$dir/$i";
while (<FILE>) {
print MERGED $_;
}
close(FILE);
}
close(MERGED);
}

sub show_diff($$) {
my $olddir = shift();
my $newdir = shift();

open diff, "diff -uw $olddir/merged $newdir/merged |";
while (<diff>) {
print $_;
}
}

my $output;
my $inside = 0;
my $olddir = tempdir();
my $newdir = tempdir();

open oldfh, ">", "$olddir/full";
open newfh, ">", "$newdir/full";

#recreate an old file and a new file
while (<>) {
if ($_ =~ /^(---|\+\+\+)/) {
next;
}

if ($_ =~ /^@/) {
$inside = 1;
}
if ($inside && !(($_ =~ /^[- @+]/) || ($_ =~ /^$/))) {
$inside = 0;
}
if (!$inside) {
next;
}

$output = filter($_);

if ($_ =~ /^-/) {
print oldfh $output;
next;
}
if ($_ =~ /^\+/) {
print newfh $output;
next;
}
print oldfh $output;
print newfh $output;

}

close(oldfh);
close(newfh);

break_out_blocks($olddir);
break_out_blocks($newdir);

replace_exact_blocks($olddir, $newdir);

merge_blocks($olddir);
merge_blocks($newdir);

show_diff($olddir, $newdir);

#print "old = $olddir/ new = $newdir/\n";
rmtree($olddir);
rmtree($newdir);