From cf655043d4ba6fb3e352d6295a6ad5c2361755c4 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Tue, 4 Mar 2008 14:28:20 -0800 Subject: update checkpatch.pl to version 0.15 This version brings a number of minor fixes updating the type detector and the unary tracker. It also brings a few small fixes for false positives. It also reverts the --file warning. Of note: - limit CVS checks to added lines - improved type detections - fixes to the unary tracker Andy Whitcroft (13): Version: 0.15 EXPORT_SYMBOL checks need to accept array variables export checks must match DECLARE_foo and LIST_HEAD possible types: cleanup debugging missing line values: track values through preprocessor conditional paths typeof is actually a type possible types: detect definitions which cross lines values: include line numbers on value debug information values: ensure we find correctly record pending brackets values: simplify the brace history stack CVS keyword checks should only apply to added lines loosen spacing for comments allow braces for single statement blocks with multiline conditionals Harvey Harrison (1): checkpatch: remove fastcall Ingo Molnar (1): checkpatch.pl: revert wrong --file message Uwe Kleine-Koenig (1): fix typo "goot" -> "good" Signed-off-by: Andy Whitcroft Cc: Randy Dunlap Cc: Joel Schopp Cc: Ingo Molnar Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 323 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 206 insertions(+), 117 deletions(-) (limited to 'scripts/checkpatch.pl') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2086a856400a..2a7cef9726e4 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -9,7 +9,7 @@ use strict; my $P = $0; $P =~ s@.*/@@g; -my $V = '0.14'; +my $V = '0.15'; use Getopt::Long qw(:config no_auto_abbrev); @@ -105,8 +105,7 @@ our $Sparse = qr{ __iomem| __must_check| __init_refok| - __kprobes| - fastcall + __kprobes }x; our $Attribute = qr{ const| @@ -158,7 +157,10 @@ sub build_types { \b (?:const\s+)? (?:unsigned\s+)? - $all + (?: + $all| + (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\) + ) (?:\s+$Sparse|\s+const)* \b }x; @@ -362,6 +364,7 @@ sub ctx_statement_block { my $type = ''; my $level = 0; + my $p; my $c; my $len = 0; @@ -386,6 +389,7 @@ sub ctx_statement_block { last; } } + $p = $c; $c = substr($blk, $off, 1); $remainder = substr($blk, $off); @@ -397,8 +401,9 @@ sub ctx_statement_block { } # An else is really a conditional as long as its not else if - if ($level == 0 && $remainder =~ /(\s+else)(?:\s|{)/ && - $remainder !~ /\s+else\s+if\b/) { + if ($level == 0 && (!defined($p) || $p =~ /(?:\s|\})/) && + $remainder =~ /(else)(?:\s|{)/ && + $remainder !~ /else\s+if\b/) { $coff = $off + length($1); } @@ -445,21 +450,73 @@ sub ctx_statement_block { $line, $remain + 1, $off - $loff + 1, $level); } +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + sub ctx_statement_full { my ($linenr, $remain, $off) = @_; my ($statement, $condition, $level); my (@chunks); + # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "F: c<$condition> s<$statement>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. for (;;) { - push(@chunks, [ $condition, $statement ]); - last if (!($remain > 0 && $condition =~ /^.\s*(?:if|else|do)/)); ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); - #print "C: c<$condition> s<$statement>\n"; + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); } return ($level, $linenr, @chunks); @@ -593,13 +650,13 @@ sub cat_vet { } my $av_preprocessor = 0; -my $av_paren = 0; +my $av_pending; my @av_paren_type; sub annotate_reset { $av_preprocessor = 0; - $av_paren = 0; - @av_paren_type = (); + $av_pending = '_'; + @av_paren_type = ('E'); } sub annotate_values { @@ -611,12 +668,13 @@ sub annotate_values { print "$stream\n" if ($dbg_values > 1); while (length($cur)) { - print " <$type> " if ($dbg_values > 1); + print " <" . join('', @av_paren_type) . + "> <$type> " if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { print "WS($1)\n" if ($dbg_values > 1); if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); $av_preprocessor = 0; - $type = 'N'; } } elsif ($cur =~ /^($Type)/) { @@ -626,11 +684,33 @@ sub annotate_values { } elsif ($cur =~ /^(#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; - $av_paren_type[$av_paren] = 'N'; + $av_pending = 'N'; - } elsif ($cur =~ /^(#\s*(?:ifdef|ifndef|if|else|elif|endif))/o) { - print "PRE($1)\n" if ($dbg_values > 1); + } elsif ($cur =~ /^(#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'N'; + + } elsif ($cur =~ /^(#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'N'; + + } elsif ($cur =~ /^(#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); $type = 'N'; } elsif ($cur =~ /^(\\\n)/o) { @@ -639,13 +719,13 @@ sub annotate_values { } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { print "SIZEOF($1)\n" if ($dbg_values > 1); if (defined $2) { - $av_paren_type[$av_paren] = 'V'; + $av_pending = 'V'; } $type = 'N'; } elsif ($cur =~ /^(if|while|typeof|__typeof__|for)\b/o) { print "COND($1)\n" if ($dbg_values > 1); - $av_paren_type[$av_paren] = 'N'; + $av_pending = 'N'; $type = 'N'; } elsif ($cur =~/^(return|case|else)/o) { @@ -654,14 +734,14 @@ sub annotate_values { } elsif ($cur =~ /^(\()/o) { print "PAREN('$1')\n" if ($dbg_values > 1); - $av_paren++; + push(@av_paren_type, $av_pending); + $av_pending = '_'; $type = 'N'; } elsif ($cur =~ /^(\))/o) { - $av_paren-- if ($av_paren > 0); - if (defined $av_paren_type[$av_paren]) { - $type = $av_paren_type[$av_paren]; - undef $av_paren_type[$av_paren]; + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; print "PAREN('$1') -> $type\n" if ($dbg_values > 1); } else { @@ -670,7 +750,7 @@ sub annotate_values { } elsif ($cur =~ /^($Ident)\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); - $av_paren_type[$av_paren] = 'V'; + $av_pending = 'V'; } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); @@ -680,11 +760,11 @@ sub annotate_values { print "ASSIGN($1)\n" if ($dbg_values > 1); $type = 'N'; - } elsif ($cur =~/^(;)/) { + } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; - } elsif ($cur =~ /^(;|{|}|\?|:|\[)/o) { + } elsif ($cur =~ /^(;|\?|:|\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; @@ -988,7 +1068,7 @@ sub process { } # check for RCS/CVS revision markers - if ($rawline =~ /\$(Revision|Log|Id)(?:\$|)/) { + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); } @@ -999,41 +1079,44 @@ sub process { # Check for potential 'bare' types if ($realcnt) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + $s =~ s/\n./ /g; + $s =~ s/{.*$//; + # Ignore goto labels. - if ($line =~ /$Ident:\*$/) { + if ($s =~ /$Ident:\*$/) { # Ignore functions being called - } elsif ($line =~ /^.\s*$Ident\s*\(/) { + } elsif ($s =~ /^.\s*$Ident\s*\(/) { # definitions in global scope can only start with types - } elsif ($line =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b/) { - possible($1, $line); + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b/) { + possible($1, $s); # declarations always start with types - } elsif ($prev_values eq 'E' && $line =~ /^.\s*(?:$Storage\s+)?(?:const\s+)?($Ident)\b(:?\s+$Sparse)?\s*\**\s*$Ident\s*(?:;|=|,)/) { - possible($1); + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:const\s+)?($Ident)\b(:?\s+$Sparse)?\s*\**\s*$Ident\s*(?:;|=|,)/) { + possible($1, $s); } # any (foo ... *) is a pointer cast, and foo is a type - while ($line =~ /\(($Ident)(?:\s+$Sparse)*\s*\*+\s*\)/g) { - possible($1, $line); + while ($s =~ /\(($Ident)(?:\s+$Sparse)*\s*\*+\s*\)/g) { + possible($1, $s); } # Check for any sort of function declaration. # int foo(something bar, other baz); # void (*store_gdt)(x86_descr_ptr *); - if ($prev_values eq 'E' && $line =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/) { + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/) { my ($name_len) = length($1); - my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, $name_len); - my $ctx = join("\n", @ctx); - $ctx =~ s/\n.//; + my $ctx = $s; substr($ctx, 0, $name_len + 1) = ''; $ctx =~ s/\)[^\)]*$//; + for my $arg (split(/\s*,\s*/, $ctx)) { if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/ || $arg =~ /^($Ident)$/) { - possible($1, $line); + possible($1, $s); } } } @@ -1100,8 +1183,8 @@ sub process { $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; - warn "--> .$outline\n"; - warn "--> $curr_values\n"; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; } $prev_values = substr($curr_values, -1); @@ -1148,7 +1231,9 @@ sub process { if (($prevline !~ /^}/) && ($prevline !~ /^\+}/) && ($prevline !~ /^ }/) && - ($prevline !~ /\b\Q$name\E(?:\s+$Attribute)?\s*(?:;|=)/)) { + ($prevline !~ /^.DECLARE_$Ident\(\Q$name\E\)/) && + ($prevline !~ /^.LIST_HEAD\(\Q$name\E\)/) && + ($prevline !~ /\b\Q$name\E(?:\s+$Attribute)?\s*(?:;|=|\[)/)) { WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); } } @@ -1266,7 +1351,7 @@ sub process { =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% }x; - my @elements = split(/($;+|$ops|;)/, $opline); + my @elements = split(/($ops|;)/, $opline); my $off = 0; my $blank = copy_spacing($opline); @@ -1277,6 +1362,7 @@ sub process { my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); $a = 'E' if ($elements[$n] eq '' && $n == 0); @@ -1287,6 +1373,7 @@ sub process { if (defined $elements[$n + 2]) { $c = 'V' if ($elements[$n + 2] ne ''); $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); $c = 'O' if ($elements[$n + 2] eq ''); $c = 'E' if ($elements[$n + 2] =~ /\s*\\$/); @@ -1330,13 +1417,13 @@ sub process { if ($op_type ne 'V' && $ca =~ /\s$/ && $cc =~ /^\s*,/) { - # Ignore comments - } elsif ($op =~ /^$;+$/) { +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { # ; should have either the end of line or a space or \ after it } elsif ($op eq ';') { - if ($ctx !~ /.x[WEB]/ && $cc !~ /^\\/ && - $cc !~ /^;/) { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { ERROR("need space after that '$op' $at\n" . $hereptr); } @@ -1351,7 +1438,7 @@ sub process { # , must have a space on the right. } elsif ($op eq ',') { - if ($ctx !~ /.xW|.xE/ && $cc !~ /^}/) { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { ERROR("need space after that '$op' $at\n" . $hereptr); } @@ -1364,7 +1451,7 @@ sub process { # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || ($is_unary && ($op eq '*' || $op eq '-' || $op eq '&'))) { - if ($ctx !~ /[WEB]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { ERROR("need space before that '$op' $at\n" . $hereptr); } if ($ctx =~ /.xW/) { @@ -1373,7 +1460,7 @@ sub process { # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WOB]x[^W]/ && $ctx !~ /[^W]x[WOBE]/) { + if ($ctx !~ /[WOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { ERROR("need space one side of that '$op' $at\n" . $hereptr); } if ($ctx =~ /WxB/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { @@ -1387,13 +1474,13 @@ sub process { $op eq '*' or $op eq '/' or $op eq '%') { - if ($ctx !~ /VxV|WxW|VxE|WxE|VxO/) { + if ($ctx !~ /VxV|WxW|VxE|WxE|VxO|Cx.|.xC/) { ERROR("need consistent spacing around '$op' $at\n" . $hereptr); } # All the others need spaces both sides. - } elsif ($ctx !~ /[EW]x[WE]/) { + } elsif ($ctx !~ /[EWC]x[CWE]/) { # Ignore email addresses if (!($op eq '<' && $cb =~ /$;\S+\@\S+>/) && !($op eq '>' && $cb =~ /<\S+\@\S+$;/)) { @@ -1551,7 +1638,7 @@ sub process { # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed -# in a known goot container +# in a known good container if ($prevline =~ /\#define.*\\/ && $prevline !~/(?:do\s+{|\(\{|\{)/ && $line !~ /(?:do\s+{|\(\{|\{)/ && @@ -1599,84 +1686,95 @@ sub process { # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, 0); + ctx_statement_full($linenr, $realcnt, 1); #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; - if ($#chunks > 1 && $level == 0) { + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks > 0 && $level == 0) { my $allowed = 0; my $seen = 0; + my $herectx = $here . "\n";; + my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; + $herectx .= "$rawlines[$ln]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + substr($block, 0, length($cond)) = ''; $seen++ if ($block =~ /^\s*{/); - $block =~ s/(^|\n)./$1/g; - $block =~ s/^\s*{//; - $block =~ s/}\s*$//; - $block =~ s/^\s*//; - $block =~ s/\s*$//; - - my @lines = ($block =~ /\n/g); - my @statements = ($block =~ /;/g); - - #print "cond<$cond> block<$block> lines<" . scalar(@lines) . "> statements<" . scalar(@statements) . "> seen<$seen> allowed<$allowed>\n"; - if (scalar(@lines) != 0) { + #print "cond<$cond> block<$block> allowed<$allowed>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } - if (scalar(@statements) > 1) { + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } } if ($seen && !$allowed) { - WARN("braces {} are not necessary for any arm of this statement\n" . $herecurr); - $suppress_ifbraces = $endln; + WARN("braces {} are not necessary for any arm of this statement\n" . $herectx); } + # Either way we have looked over this whole + # statement and said what needs to be said. + $suppress_ifbraces = $endln; } } if ($linenr > $suppress_ifbraces && $line =~ /\b(if|while|for|else)\b/) { - # Locate the end of the opening statement. - my @control = ctx_statement($linenr, $realcnt, 0); - my $nr = $linenr + (scalar(@control) - 1); - my $cnt = $realcnt - (scalar(@control) - 1); - - my $off = $realcnt - $cnt; - #print "$off: line<$line>end<" . $lines[$nr - 1] . ">\n"; - - # If this is is a braced statement group check it - if ($lines[$nr - 1] =~ /{\s*$/) { - my ($lvl, @block) = ctx_block_level($nr, $cnt); - - my $stmt = join("\n", @block); - # Drop the diff line leader. - $stmt =~ s/\n./\n/g; - # Drop the code outside the block. - $stmt =~ s/(^[^{]*){\s*//; - my $before = $1; - $stmt =~ s/\s*}([^}]*$)//; - my $after = $1; - - #print "block<" . join(' ', @block) . "><" . scalar(@block) . ">\n"; - #print "before<$before> stmt<$stmt> after<$after>\n\n"; - - # Count the newlines, if there is only one - # then the block should not have {}'s. - my @lines = ($stmt =~ /\n/g); - my @statements = ($stmt =~ /;/g); - #print "lines<" . scalar(@lines) . ">\n"; - #print "statements<" . scalar(@statements) . ">\n"; - if ($lvl == 0 && scalar(@lines) == 0 && - scalar(@statements) < 2 && - $stmt !~ /{/ && $stmt !~ /\bif\b/ && - $before !~ /}/ && $after !~ /{/) { - my $herectx = "$here\n" . join("\n", @control, @block[1 .. $#block]) . "\n"; - shift(@block); - WARN("braces {} are not necessary for single statement blocks\n" . $herectx); + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + if (defined $cond) { + substr($block, 0, length($cond)) = ''; + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond)) = ''; + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n";; + my $end = $linenr + statement_rawlines($block) - 1; + + for (my $ln = $linenr - 1; $ln < $end; $ln++) { + $herectx .= $rawlines[$ln] . "\n";; } + + WARN("braces {} are not necessary for single statement blocks\n" . $herectx); } } @@ -1828,15 +1926,6 @@ sub process { print "are false positives report them to the maintainer, see\n"; print "CHECKPATCH in MAINTAINERS.\n"; } - print < Date: Fri, 28 Mar 2008 14:15:58 -0700 Subject: update checkpatch.pl to version 0.16 This version brings proper quote tracking across lines, and brings the handling of comments into the same mechanism ensuring nesting is correctly handled. It brings the usual flurry of fixes for false positives. It also brings a number of new checks. The most contentious change will likely be the checks for NR_CPUS as this throws some new warnings in kernel/sched.c. Of note: - all new quote tracking across lines - all new comment tracking - new more direct, less ambigious wording for some warnings - recommends mutexes and completions over semaphores - recommends strict_strto* over simple_strto* - report on direct use of NR_CPUS Andy Whitcroft (22): Version: 0.16 string quote tracking should cross line boundaries check spacing round -> correctly across newlines checks for linux/ against asm/ include files should be warnings standardise on 'required' and 'prohibited' take the first end of condition when parsing statements values: cope with unbalanced brackets preprocessor #elif is not a function preprocessor #if should not trigger trailing statement checks test: allow us to limit output to a single error recommend real mutexes over semaphores asm checks should mirror those for __asm__ warn on semaphores being used in place of completions trailing ; on control structure should ignore do {} while (); recommend strict_strtoX over simple_strtoX redo comment handling as a quote type use of NR_CPUS is normally wrong consistant spacing should only be about spaces if brace check suppression should only apply to the top-levels use tr/// to align spacing for operators move to using four parameter form of substr check and report modifications to include/asm Signed-off-by: Andy Whitcroft Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 463 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 279 insertions(+), 184 deletions(-) (limited to 'scripts/checkpatch.pl') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2a7cef9726e4..58a94947d655 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -9,7 +9,7 @@ use strict; my $P = $0; $P =~ s@.*/@@g; -my $V = '0.15'; +my $V = '0.16'; use Getopt::Long qw(:config no_auto_abbrev); @@ -18,6 +18,7 @@ my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; my $tst_type = 0; +my $tst_only; my $emacs = 0; my $terse = 0; my $file = 0; @@ -44,6 +45,7 @@ GetOptions( 'debug=s' => \%debug, 'test-type!' => \$tst_type, + 'test-only=s' => \$tst_only, ) or exit; my $exit = 0; @@ -263,17 +265,7 @@ sub expand_tabs { return $res; } sub copy_spacing { - my ($str) = @_; - - my $res = ''; - for my $c (split(//, $str)) { - if ($c eq "\t") { - $res .= $c; - } else { - $res .= ' '; - } - } - + (my $res = shift) =~ tr/\t/ /c; return $res; } @@ -290,53 +282,76 @@ sub line_stats { return (length($line), length($white)); } +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} sub sanitise_line { my ($line) = @_; my $res = ''; my $l = ''; - my $quote = ''; my $qlen = 0; + my $off = 0; + my $c; - foreach my $c (split(//, $line)) { - # The second backslash of a pair is not a "quote". - if ($l eq "\\" && $c eq "\\") { - $c = 'X'; - } - if ($l ne "\\" && ($c eq "'" || $c eq '"')) { - if ($quote eq '') { - $quote = $c; - $res .= $c; - $l = $c; - $qlen = 0; - next; - } elsif ($quote eq $c) { - $quote = ''; - } + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; } - if ($quote eq "'" && $qlen > 1) { - $quote = ''; + if (substr($line, $off, 2) eq $sanitise_quote) { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; } - if ($quote && $c ne "\t") { - $res .= "X"; - $qlen++; - } else { - $res .= $c; + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; - $l = $c; - } + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } - # Clear out the comments. - while ($res =~ m@(/\*.*?\*/)@g) { - substr($res, $-[1], $+[1] - $-[1]) = $; x ($+[1] - $-[1]); - } - if ($res =~ m@(/\*.*)@) { - substr($res, $-[1], $+[1] - $-[1]) = $; x ($+[1] - $-[1]); - } - if ($res =~ m@^.(.*\*/)@) { - substr($res, $-[1], $+[1] - $-[1]) = $; x ($+[1] - $-[1]); + #print "SQ:$sanitise_quote\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } } # The pathname on a #include may be surrounded by '<' and '>'. @@ -359,6 +374,7 @@ sub ctx_statement_block { my $blk = ''; my $soff = $off; my $coff = $off - 1; + my $coff_set = 0; my $loff = 0; @@ -370,7 +386,7 @@ sub ctx_statement_block { my $remainder; while (1) { - #warn "CSB: blk<$blk>\n"; + #warn "CSB: blk<$blk> remain<$remain>\n"; # If we are about to drop off the end, pull in more # context. if ($off >= $len) { @@ -393,7 +409,7 @@ sub ctx_statement_block { $c = substr($blk, $off, 1); $remainder = substr($blk, $off); - #warn "CSB: c<$c> type<$type> level<$level>\n"; + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; # Statement ends at the ';' or a close '}' at the # outermost level. if ($level == 0 && $c eq ';') { @@ -401,10 +417,14 @@ sub ctx_statement_block { } # An else is really a conditional as long as its not else if - if ($level == 0 && (!defined($p) || $p =~ /(?:\s|\})/) && - $remainder =~ /(else)(?:\s|{)/ && - $remainder !~ /else\s+if\b/) { - $coff = $off + length($1); + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; } if (($type eq '' || $type eq '(') && $c eq '(') { @@ -417,6 +437,8 @@ sub ctx_statement_block { if ($level == 0 && $coff < $soff) { $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; } } if (($type eq '' || $type eq '{') && $c eq '{') { @@ -444,7 +466,7 @@ sub ctx_statement_block { #warn "STATEMENT<$statement>\n"; #warn "CONDITION<$condition>\n"; - #print "off<$off> loff<$loff>\n"; + #print "coff<$coff> soff<$off> loff<$loff>\n"; return ($statement, $condition, $line, $remain + 1, $off - $loff + 1, $level); @@ -502,7 +524,7 @@ sub ctx_statement_full { # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); - #print "F: c<$condition> s<$statement>\n"; + #print "F: c<$condition> s<$statement> remain<$remain>\n"; push(@chunks, [ $condition, $statement ]); if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { return ($level, $linenr, @chunks); @@ -514,7 +536,7 @@ sub ctx_statement_full { ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "C: c<$condition> s<$statement> remain<$remain>\n"; - last if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:else|do)\b/s)); + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); #print "C: push\n"; push(@chunks, [ $condition, $statement ]); } @@ -668,6 +690,7 @@ sub annotate_values { print "$stream\n" if ($dbg_values > 1); while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); print " <" . join('', @av_paren_type) . "> <$type> " if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { @@ -804,28 +827,34 @@ sub possible { my $prefix = ''; sub report { + if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { + return 0; + } my $line = $prefix . $_[0]; $line = (split('\n', $line))[0] . "\n" if ($terse); push(our @report, $line); + + return 1; } sub report_dump { our @report; } sub ERROR { - report("ERROR: $_[0]\n"); - our $clean = 0; - our $cnt_error++; + if (report("ERROR: $_[0]\n")) { + our $clean = 0; + our $cnt_error++; + } } sub WARN { - report("WARNING: $_[0]\n"); - our $clean = 0; - our $cnt_warn++; + if (report("WARNING: $_[0]\n")) { + our $clean = 0; + our $cnt_warn++; + } } sub CHK { - if ($check) { - report("CHECK: $_[0]\n"); + if ($check && report("CHECK: $_[0]\n")) { our $clean = 0; our $cnt_chk++; } @@ -867,30 +896,76 @@ sub process { my $prev_values = 'E'; # suppression flags - my $suppress_ifbraces = 0; + my %suppress_ifbraces; # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # my @setup_docs = (); my $setup_docs = 0; + + sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { - # Standardise the strings and chars within the input to - # simplify matching. - $line = sanitise_line($rawline); - push(@lines, $line); - - ##print "==>$rawline\n"; - ##print "-->$line\n"; + $linenr++; + $line = $rawline; - if ($line=~/^\+\+\+\s+(\S+)/) { + if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/kernel-parameters.txt$@) { $setup_docs = 1; } - next; + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + for (my $ln = $linenr; $ln < ($linenr + $realcnt); $ln++) { + next if ($line =~ /^-/); + ($edge) = ($rawlines[$ln - 1] =~ m@(/\*|\*/)@); + last if (defined $edge); + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s* \*(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt) { + # Standardise the strings and chars within the input to + # simplify matching. + $line = sanitise_line($rawline); } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; if ($setup_docs && $line =~ /^\+/) { push(@setup_docs, $line); @@ -899,23 +974,17 @@ sub process { $prefix = ''; + $realcnt = 0; + $linenr = 0; foreach my $line (@lines) { $linenr++; my $rawline = $rawlines[$linenr - 1]; -#extract the filename as it passes - if ($line=~/^\+\+\+\s+(\S+)/) { - $realfile=$1; - $realfile =~ s@^[^/]*/@@; - $in_comment = 0; - next; - } #extract the line range in the file after the patch is applied if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $is_patch = 1; $first_line = $linenr + 1; - $in_comment = 0; $realline=$1-1; if (defined $2) { $realcnt=$3+1; @@ -925,50 +994,16 @@ sub process { annotate_reset(); $prev_values = 'E'; - $suppress_ifbraces = $linenr - 1; + %suppress_ifbraces = (); next; - } # track the line number as we move through the hunk, note that # new versions of GNU diff omit the leading space on completely # blank context lines so we need to count that too. - if ($line =~ /^( |\+|$)/) { + } elsif ($line =~ /^( |\+|$)/) { $realline++; $realcnt-- if ($realcnt != 0); - # Guestimate if this is a continuing comment. Run - # the context looking for a comment "edge". If this - # edge is a close comment then we must be in a comment - # at context start. - if ($linenr == $first_line) { - my $edge; - for (my $ln = $first_line; $ln < ($linenr + $realcnt); $ln++) { - ($edge) = ($rawlines[$ln - 1] =~ m@(/\*|\*/)@); - last if (defined $edge); - } - if (defined $edge && $edge eq '*/') { - $in_comment = 1; - } - } - - # Guestimate if this is a continuing comment. If this - # is the start of a diff block and this line starts - # ' *' then it is very likely a comment. - if ($linenr == $first_line and $rawline =~ m@^.\s* \*(?:\s|$)@) { - $in_comment = 1; - } - - # Find the last comment edge on _this_ line. - $comment_edge = 0; - while (($rawline =~ m@(/\*|\*/)@g)) { - if ($1 eq '/*') { - $in_comment = 1; - } else { - $in_comment = 0; - } - $comment_edge = 1; - } - # Measure the line length and indent. ($length, $indent) = line_stats($rawline); @@ -977,23 +1012,36 @@ sub process { ($previndent, $stashindent) = ($stashindent, $indent); ($prevrawline, $stashrawline) = ($stashrawline, $rawline); - #warn "ic<$in_comment> ce<$comment_edge> line<$line>\n"; + #warn "line<$line>\n"; } elsif ($realcnt == 1) { $realcnt--; } #make up the handle for any error we report on this line + $prefix = "$filename:$realline: " if ($emacs && $file); + $prefix = "$filename:$linenr: " if ($emacs && !$file); + $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); + + # extract the filename as it passes + if ($line=~/^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^[^/]*/@@; + + if ($realfile =~ m@include/asm/@) { + ERROR("do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); + } + next; + } + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; - $prefix = "$filename:$realline: " if ($emacs && $file); - $prefix = "$filename:$linenr: " if ($emacs && !$file); $cnt_lines++ if ($realcnt != 0); #check the patch for a signoff: @@ -1005,7 +1053,7 @@ sub process { $herecurr); } if ($line =~ /^\s*signed-off-by:\S/i) { - WARN("need space after Signed-off-by:\n" . + WARN("space required after Signed-off-by:\n" . $herecurr); } } @@ -1072,11 +1120,6 @@ sub process { WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# The rest of our checks refer specifically to C style -# only apply those _outside_ comments. Only skip -# lines in the middle of comments. - next if (!$comment_edge && $in_comment); - # Check for potential 'bare' types if ($realcnt) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); @@ -1110,7 +1153,7 @@ sub process { my ($name_len) = length($1); my $ctx = $s; - substr($ctx, 0, $name_len + 1) = ''; + substr($ctx, 0, $name_len + 1, ''); $ctx =~ s/\)[^\)]*$//; for my $arg (split(/\s*,\s*/, $ctx)) { @@ -1151,27 +1194,33 @@ sub process { # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else - if ($line =~ /\b(?:(if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.#/) { + if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.#/) { + my $pre_ctx = "$1$2"; + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); my $ctx_ln = $linenr + $#ctx + 1; my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); + ##warn "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + # Skip over any removed lines in the context following statement. - while ($ctx_cnt > 0 && $lines[$ctx_ln - 1] =~ /^-/) { + while (defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^-/) { $ctx_ln++; - $ctx_cnt--; } - ##warn "line<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>"; + ##warn "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - if ($ctx !~ /{\s*/ && $ctx_cnt > 0 && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { - ERROR("That open brace { should be on the previous line\n" . + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("that open brace { should be on the previous line\n" . "$here\n$ctx\n$lines[$ctx_ln - 1]"); } - if ($level == 0 && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); if ($nindent > $indent) { - WARN("Trailing semicolon indicates no statements, indent implies otherwise\n" . + WARN("trailing semicolon indicates no statements, indent implies otherwise\n" . "$here\n$ctx\n$lines[$ctx_ln - 1]"); } } @@ -1200,7 +1249,7 @@ sub process { # check for initialisation to aggregates open brace on the next line if ($prevline =~ /$Declare\s*$Ident\s*=\s*$/ && $line =~ /^.\s*{/) { - ERROR("That open brace { should be on the previous line\n" . $hereprev); + ERROR("that open brace { should be on the previous line\n" . $hereprev); } # @@ -1325,22 +1374,31 @@ sub process { # check for spaces between functions and their parentheses. while ($line =~ /($Ident)\s+\(/g) { my $name = $1; - my $ctx = substr($line, 0, $-[1]); + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; # Ignore those directives where spaces _are_ permitted. - if ($name =~ /^(?:if|for|while|switch|return|volatile|__volatile__|__attribute__|format|__extension__|Copyright|case|__asm__)$/) { + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. - } elsif ($ctx =~ /^.\#\s*define\s*$/) { + } elsif ($ctx_before =~ /^.\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\#\s*elif\s*$/) { # If this whole things ends with a type its most # likely a typedef for a function. - } elsif ("$ctx$name" =~ /$Type$/) { + } elsif ($ctx =~ /$Type$/) { } else { - WARN("no space between function name and open parenthesis '('\n" . $herecurr); + WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr); } } # Check operator spacing. @@ -1359,13 +1417,21 @@ sub process { for (my $n = 0; $n < $#elements; $n += 2) { $off += length($elements[$n]); + # Pick up the preceeding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); - $a = 'E' if ($elements[$n] eq '' && $n == 0); + $a = 'E' if ($ca =~ /^\s*$/); my $op = $elements[$n + 1]; @@ -1381,14 +1447,6 @@ sub process { $c = 'E'; } - # Pick up the preceeding and succeeding characters. - my $ca = substr($opline, 0, $off); - my $cc = ''; - if (length($opline) >= ($off + length($elements[$n + 1]))) { - $cc = substr($opline, $off + length($elements[$n + 1])); - } - my $cb = "$ca$;$cc"; - my $ctx = "${a}x${c}"; my $at = "(ctx:$ctx)"; @@ -1424,7 +1482,7 @@ sub process { } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { - ERROR("need space after that '$op' $at\n" . $hereptr); + ERROR("space required after that '$op' $at\n" . $hereptr); } # // is a comment @@ -1433,13 +1491,13 @@ sub process { # -> should have no spaces } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { - ERROR("no spaces around that '$op' $at\n" . $hereptr); + ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); } # , must have a space on the right. } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - ERROR("need space after that '$op' $at\n" . $hereptr); + ERROR("space required after that '$op' $at\n" . $hereptr); } # '*' as part of a type definition -- reported already. @@ -1452,21 +1510,26 @@ sub process { } elsif ($op eq '!' || $op eq '~' || ($is_unary && ($op eq '*' || $op eq '-' || $op eq '&'))) { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - ERROR("need space before that '$op' $at\n" . $hereptr); + ERROR("space required before that '$op' $at\n" . $hereptr); } if ($ctx =~ /.xW/) { - ERROR("no space after that '$op' $at\n" . $hereptr); + ERROR("space prohibited after that '$op' $at\n" . $hereptr); } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - ERROR("need space one side of that '$op' $at\n" . $hereptr); + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + ERROR("space required one side of that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); } - if ($ctx =~ /WxB/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { - ERROR("no space before that '$op' $at\n" . $hereptr); + if ($ctx =~ /ExW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); } + # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or @@ -1474,7 +1537,7 @@ sub process { $op eq '*' or $op eq '/' or $op eq '%') { - if ($ctx !~ /VxV|WxW|VxE|WxE|VxO|Cx.|.xC/) { + if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { ERROR("need consistent spacing around '$op' $at\n" . $hereptr); } @@ -1484,7 +1547,7 @@ sub process { # Ignore email addresses if (!($op eq '<' && $cb =~ /$;\S+\@\S+>/) && !($op eq '>' && $cb =~ /<\S+\@\S+$;/)) { - ERROR("need spaces around that '$op' $at\n" . $hereptr); + ERROR("spaces required around that '$op' $at\n" . $hereptr); } } $off += length($elements[$n + 1]); @@ -1514,31 +1577,31 @@ sub process { #need space before brace following if, while, etc if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || $line =~ /do{/) { - ERROR("need a space before the open brace '{'\n" . $herecurr); + ERROR("space required before the open brace '{'\n" . $herecurr); } # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { - ERROR("need a space after that close brace '}'\n" . $herecurr); + ERROR("space required after that close brace '}'\n" . $herecurr); } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - ERROR("no space after that open square bracket '['\n" . $herecurr); + ERROR("space prohibited after that open square bracket '['\n" . $herecurr); } if ($line =~ /\s\]/) { - ERROR("no space before that close square bracket ']'\n" . $herecurr); + ERROR("space prohibited before that close square bracket ']'\n" . $herecurr); } # check spacing on paretheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { - ERROR("no space after that open parenthesis '('\n" . $herecurr); + ERROR("space prohibited after that open parenthesis '('\n" . $herecurr); } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/) { - ERROR("no space before that close parenthesis ')'\n" . $herecurr); + ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr); } #goto labels aren't indented, allow a single space however @@ -1549,7 +1612,7 @@ sub process { # Need a space before open parenthesis after if, while etc if ($line=~/\b(if|while|for|switch)\(/) { - ERROR("need a space before the open parenthesis '('\n" . $herecurr); + ERROR("space required before the open parenthesis '('\n" . $herecurr); } # Check for illegal assignment in if conditional. @@ -1562,10 +1625,12 @@ sub process { # Find out what is on the end of the line after the # conditional. - substr($s, 0, length($c)) = ''; + substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*({|;|)\s*\\*\s*$/) { + if (length($c) && $s !~ /^\s*({|;|)\s*\\*\s*$/ && + $c !~ /^.\#\s*if/) + { ERROR("trailing statements should be on next line\n" . $herecurr); } } @@ -1607,7 +1672,7 @@ sub process { # Find out what is on the end of the line after the # conditional. - substr($s, 0, length($c)) = ''; + substr($s, 0, length($c), ''); $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { @@ -1631,7 +1696,7 @@ sub process { if ($tree && $rawline =~ m{^.\#\s*include\s*\}) { my $checkfile = "$root/include/linux/$1.h"; if (-f $checkfile && $1 ne 'irq.h') { - CHK("Use #include instead of \n" . + WARN("Use #include instead of \n" . $herecurr); } } @@ -1692,15 +1757,24 @@ sub process { if ($#chunks > 0 && $level == 0) { my $allowed = 0; my $seen = 0; - my $herectx = $here . "\n";; + my $herectx = $here . "\n"; my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; - $herectx .= "$rawlines[$ln]\n[...]\n"; + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; $ln += statement_rawlines($block) - 1; - substr($block, 0, length($cond)) = ''; + substr($block, 0, length($cond), ''); $seen++ if ($block =~ /^\s*{/); @@ -1721,16 +1795,10 @@ sub process { if ($seen && !$allowed) { WARN("braces {} are not necessary for any arm of this statement\n" . $herectx); } - # Either way we have looked over this whole - # statement and said what needs to be said. - $suppress_ifbraces = $endln; } } - if ($linenr > $suppress_ifbraces && + if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); - my $allowed = 0; # Check the pre-context. @@ -1738,10 +1806,15 @@ sub process { #print "APW: ALLOWED: pre<$1>\n"; $allowed = 1; } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + # Check the condition. my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; if (defined $cond) { - substr($block, 0, length($cond)) = ''; + substr($block, 0, length($cond), ''); } if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; @@ -1759,7 +1832,7 @@ sub process { if (defined $chunks[1]) { my ($cond, $block) = @{$chunks[1]}; if (defined $cond) { - substr($block, 0, length($cond)) = ''; + substr($block, 0, length($cond), ''); } if ($block =~ /^\s*\{/) { #print "APW: ALLOWED: chunk-1 block<$block>\n"; @@ -1882,6 +1955,28 @@ sub process { if ($line =~ /__FUNCTION__/) { WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); } + +# check for semaphores used as mutexes + if ($line =~ /\b(DECLARE_MUTEX|init_MUTEX)\s*\(/) { + WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); + } +# check for semaphores used as mutexes + if ($line =~ /\binit_MUTEX_LOCKED\s*\(/) { + WARN("consider using a completion\n" . $herecurr); + } +# recommend strict_strto* over simple_strto* + if ($line =~ /\bsimple_(strto.*?)\s*\(/) { + WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.#\s*define\s+NR_CPUS\s+/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/) + { + WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } } # If we have no input at all, then there is nothing to report on -- cgit v1.2.3