useless-if-before-free 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. eval '(exit $?0)' && eval 'exec perl -wST "$0" ${1+"$@"}'
  2. & eval 'exec perl -wST "$0" $argv:q'
  3. if 0;
  4. # Detect instances of "if (p) free (p);".
  5. # Likewise "if (p != 0)", "if (0 != p)", or with NULL; and with braces.
  6. my $VERSION = '2011-04-20 13:43'; # UTC
  7. # The definition above must lie within the first 8 lines in order
  8. # for the Emacs time-stamp write hook (at end) to update it.
  9. # If you change this file with Emacs, please let the write hook
  10. # do its job. Otherwise, update this string manually.
  11. # Copyright (C) 2008-2011 Free Software Foundation, Inc.
  12. # This program is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation, either version 3 of the License, or
  15. # (at your option) any later version.
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. # You should have received a copy of the GNU General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. # Written by Jim Meyering
  23. use strict;
  24. use warnings;
  25. use Getopt::Long;
  26. (my $ME = $0) =~ s|.*/||;
  27. # use File::Coda; # http://meyering.net/code/Coda/
  28. END {
  29. defined fileno STDOUT or return;
  30. close STDOUT and return;
  31. warn "$ME: failed to close standard output: $!\n";
  32. $? ||= 1;
  33. }
  34. sub usage ($)
  35. {
  36. my ($exit_code) = @_;
  37. my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
  38. if ($exit_code != 0)
  39. {
  40. print $STREAM "Try `$ME --help' for more information.\n";
  41. }
  42. else
  43. {
  44. print $STREAM <<EOF;
  45. Usage: $ME [OPTIONS] FILE...
  46. Detect any instance in FILE of a useless "if" test before a free call, e.g.,
  47. "if (p) free (p);". Any such test may be safely removed without affecting
  48. the semantics of the C code in FILE. Use --name=FOO --name=BAR to also
  49. detect free-like functions named FOO and BAR.
  50. OPTIONS:
  51. --list print only the name of each matching FILE (\\0-terminated)
  52. --name=N add name N to the list of \`free\'-like functions to detect;
  53. may be repeated
  54. --help display this help and exit
  55. --version output version information and exit
  56. Exit status:
  57. 0 one or more matches
  58. 1 no match
  59. 2 an error
  60. EXAMPLE:
  61. For example, this command prints all removable "if" tests before "free"
  62. and "kfree" calls in the linux kernel sources:
  63. git ls-files -z |xargs -0 $ME --name=kfree
  64. EOF
  65. }
  66. exit $exit_code;
  67. }
  68. sub is_NULL ($)
  69. {
  70. my ($expr) = @_;
  71. return ($expr eq 'NULL' || $expr eq '0');
  72. }
  73. {
  74. sub EXIT_MATCH {0}
  75. sub EXIT_NO_MATCH {1}
  76. sub EXIT_ERROR {2}
  77. my $err = EXIT_NO_MATCH;
  78. my $list;
  79. my @name = qw(free);
  80. GetOptions
  81. (
  82. help => sub { usage 0 },
  83. version => sub { print "$ME version $VERSION\n"; exit },
  84. list => \$list,
  85. 'name=s@' => \@name,
  86. ) or usage 1;
  87. # Make sure we have the right number of non-option arguments.
  88. # Always tell the user why we fail.
  89. @ARGV < 1
  90. and (warn "$ME: missing FILE argument\n"), usage EXIT_ERROR;
  91. my $or = join '|', @name;
  92. my $regexp = qr/(?:$or)/;
  93. # Set the input record separator.
  94. # Note: this makes it impractical to print line numbers.
  95. $/ = '"';
  96. my $found_match = 0;
  97. FILE:
  98. foreach my $file (@ARGV)
  99. {
  100. open FH, '<', $file
  101. or (warn "$ME: can't open `$file' for reading: $!\n"),
  102. $err = EXIT_ERROR, next;
  103. while (defined (my $line = <FH>))
  104. {
  105. while ($line =~
  106. /\b(if\s*\(\s*([^)]+?)(?:\s*!=\s*([^)]+?))?\s*\)
  107. # 1 2 3
  108. (?: \s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;|
  109. \s*\{\s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;\s*\}))/sxg)
  110. {
  111. my $all = $1;
  112. my ($lhs, $rhs) = ($2, $3);
  113. my ($free_opnd, $braced_free_opnd) = ($4, $5);
  114. my $non_NULL;
  115. if (!defined $rhs) { $non_NULL = $lhs }
  116. elsif (is_NULL $rhs) { $non_NULL = $lhs }
  117. elsif (is_NULL $lhs) { $non_NULL = $rhs }
  118. else { next }
  119. # Compare the non-NULL part of the "if" expression and the
  120. # free'd expression, without regard to white space.
  121. $non_NULL =~ tr/ \t//d;
  122. my $e2 = defined $free_opnd ? $free_opnd : $braced_free_opnd;
  123. $e2 =~ tr/ \t//d;
  124. if ($non_NULL eq $e2)
  125. {
  126. $found_match = 1;
  127. $list
  128. and (print "$file\0"), next FILE;
  129. print "$file: $all\n";
  130. }
  131. }
  132. }
  133. }
  134. continue
  135. {
  136. close FH;
  137. }
  138. $found_match && $err == EXIT_NO_MATCH
  139. and $err = EXIT_MATCH;
  140. exit $err;
  141. }
  142. my $foo = <<'EOF';
  143. # The above is to *find* them.
  144. # This adjusts them, removing the unnecessary "if (p)" part.
  145. # FIXME: do something like this as an option (doesn't do braces):
  146. free=xfree
  147. git grep -l -z "$free *(" \
  148. | xargs -0 useless-if-before-free -l --name="$free" \
  149. | xargs -0 perl -0x3b -pi -e \
  150. 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s+('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\)\s*;)/$2/s'
  151. # Use the following to remove redundant uses of kfree inside braces.
  152. # Note that -0777 puts perl in slurp-whole-file mode;
  153. # but we have plenty of memory, these days...
  154. free=kfree
  155. git grep -l -z "$free *(" \
  156. | xargs -0 useless-if-before-free -l --name="$free" \
  157. | xargs -0 perl -0777 -pi -e \
  158. 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s*\{\s*('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\);)\s*\}[^\n]*$/$2/gms'
  159. Be careful that the result of the above transformation is valid.
  160. If the matched string is followed by "else", then obviously, it won't be.
  161. When modifying files, refuse to process anything other than a regular file.
  162. EOF
  163. ## Local Variables:
  164. ## mode: perl
  165. ## indent-tabs-mode: nil
  166. ## eval: (add-hook 'write-file-hooks 'time-stamp)
  167. ## time-stamp-start: "my $VERSION = '"
  168. ## time-stamp-format: "%:y-%02m-%02d %02H:%02M"
  169. ## time-stamp-time-zone: "UTC"
  170. ## time-stamp-end: "'; # UTC"
  171. ## End: