]>
Commit | Line | Data |
---|---|---|
2dbf1bf8 | 1 | #!/usr/bin/env perl |
a35beedf BB |
2 | # |
3 | # CDDL HEADER START | |
4 | # | |
5 | # The contents of this file are subject to the terms of the | |
6 | # Common Development and Distribution License (the "License"). | |
7 | # You may not use this file except in compliance with the License. | |
8 | # | |
9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
10 | # or http://www.opensolaris.org/os/licensing. | |
11 | # See the License for the specific language governing permissions | |
12 | # and limitations under the License. | |
13 | # | |
14 | # When distributing Covered Code, include this CDDL HEADER in each | |
15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
16 | # If applicable, add the following below this CDDL HEADER, with the | |
17 | # fields enclosed by brackets "[]" replaced with your own identifying | |
18 | # information: Portions Copyright [yyyy] [name of copyright owner] | |
19 | # | |
20 | # CDDL HEADER END | |
21 | # | |
d96e5439 | 22 | # Copyright 2016 Nexenta Systems, Inc. |
a35beedf BB |
23 | # |
24 | # Copyright 2008 Sun Microsystems, Inc. All rights reserved. | |
25 | # Use is subject to license terms. | |
26 | # | |
27 | # @(#)cstyle 1.58 98/09/09 (from shannon) | |
28 | #ident "%Z%%M% %I% %E% SMI" | |
29 | # | |
30 | # cstyle - check for some common stylistic errors. | |
31 | # | |
32 | # cstyle is a sort of "lint" for C coding style. | |
33 | # It attempts to check for the style used in the | |
34 | # kernel, sometimes known as "Bill Joy Normal Form". | |
35 | # | |
36 | # There's a lot this can't check for, like proper indentation | |
37 | # of code blocks. There's also a lot more this could check for. | |
38 | # | |
39 | # A note to the non perl literate: | |
40 | # | |
41 | # perl regular expressions are pretty much like egrep | |
42 | # regular expressions, with the following special symbols | |
43 | # | |
44 | # \s any space character | |
45 | # \S any non-space character | |
46 | # \w any "word" character [a-zA-Z0-9_] | |
47 | # \W any non-word character | |
48 | # \d a digit [0-9] | |
49 | # \D a non-digit | |
50 | # \b word boundary (between \w and \W) | |
51 | # \B non-word boundary | |
52 | # | |
53 | ||
54 | require 5.0; | |
2dbf1bf8 | 55 | use warnings; |
a35beedf BB |
56 | use IO::File; |
57 | use Getopt::Std; | |
58 | use strict; | |
59 | ||
60 | my $usage = | |
42d4a8e5 | 61 | "usage: cstyle [-cghpvCP] [-o constructs] file ... |
a35beedf | 62 | -c check continuation indentation inside functions |
42d4a8e5 | 63 | -g print github actions' workflow commands |
a35beedf BB |
64 | -h perform heuristic checks that are sometimes wrong |
65 | -p perform some of the more picky checks | |
66 | -v verbose | |
67 | -C don't check anything in header block comments | |
68 | -P check for use of non-POSIX types | |
69 | -o constructs | |
4e33ba4c | 70 | allow a comma-separated list of optional constructs: |
a35beedf BB |
71 | doxygen allow doxygen-style block comments (/** /*!) |
72 | splint allow splint-style lint comments (/*@ ... @*/) | |
73 | "; | |
74 | ||
75 | my %opts; | |
76 | ||
42d4a8e5 | 77 | if (!getopts("cgho:pvCP", \%opts)) { |
a35beedf BB |
78 | print $usage; |
79 | exit 2; | |
80 | } | |
81 | ||
82 | my $check_continuation = $opts{'c'}; | |
42d4a8e5 | 83 | my $github_workflow = $opts{'g'} || $ENV{'CI'}; |
a35beedf BB |
84 | my $heuristic = $opts{'h'}; |
85 | my $picky = $opts{'p'}; | |
86 | my $verbose = $opts{'v'}; | |
87 | my $ignore_hdr_comment = $opts{'C'}; | |
88 | my $check_posix_types = $opts{'P'}; | |
89 | ||
90 | my $doxygen_comments = 0; | |
91 | my $splint_comments = 0; | |
92 | ||
93 | if (defined($opts{'o'})) { | |
94 | for my $x (split /,/, $opts{'o'}) { | |
95 | if ($x eq "doxygen") { | |
96 | $doxygen_comments = 1; | |
97 | } elsif ($x eq "splint") { | |
98 | $splint_comments = 1; | |
99 | } else { | |
100 | print "cstyle: unrecognized construct \"$x\"\n"; | |
101 | print $usage; | |
102 | exit 2; | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | my ($filename, $line, $prev); # shared globals | |
108 | ||
109 | my $fmt; | |
110 | my $hdr_comment_start; | |
111 | ||
112 | if ($verbose) { | |
113 | $fmt = "%s: %d: %s\n%s\n"; | |
114 | } else { | |
115 | $fmt = "%s: %d: %s\n"; | |
116 | } | |
117 | ||
118 | if ($doxygen_comments) { | |
119 | # doxygen comments look like "/*!" or "/**"; allow them. | |
120 | $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; | |
121 | } else { | |
122 | $hdr_comment_start = qr/^\s*\/\*$/; | |
123 | } | |
124 | ||
125 | # Note, following must be in single quotes so that \s and \w work right. | |
126 | my $typename = '(int|char|short|long|unsigned|float|double' . | |
127 | '|\w+_t|struct\s+\w+|union\s+\w+|FILE)'; | |
128 | ||
129 | # mapping of old types to POSIX compatible types | |
130 | my %old2posix = ( | |
131 | 'unchar' => 'uchar_t', | |
132 | 'ushort' => 'ushort_t', | |
133 | 'uint' => 'uint_t', | |
134 | 'ulong' => 'ulong_t', | |
135 | 'u_int' => 'uint_t', | |
136 | 'u_short' => 'ushort_t', | |
137 | 'u_long' => 'ulong_t', | |
138 | 'u_char' => 'uchar_t', | |
139 | 'quad' => 'quad_t' | |
140 | ); | |
141 | ||
142 | my $lint_re = qr/\/\*(?: | |
15b98249 | 143 | NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| |
a35beedf BB |
144 | CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| |
145 | FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| | |
146 | PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*? | |
147 | )\*\//x; | |
148 | ||
149 | my $splint_re = qr/\/\*@.*?@\*\//x; | |
150 | ||
151 | my $warlock_re = qr/\/\*\s*(?: | |
152 | VARIABLES\ PROTECTED\ BY| | |
153 | MEMBERS\ PROTECTED\ BY| | |
154 | ALL\ MEMBERS\ PROTECTED\ BY| | |
155 | READ-ONLY\ VARIABLES:| | |
156 | READ-ONLY\ MEMBERS:| | |
157 | VARIABLES\ READABLE\ WITHOUT\ LOCK:| | |
158 | MEMBERS\ READABLE\ WITHOUT\ LOCK:| | |
159 | LOCKS\ COVERED\ BY| | |
160 | LOCK\ UNNEEDED\ BECAUSE| | |
161 | LOCK\ NEEDED:| | |
162 | LOCK\ HELD\ ON\ ENTRY:| | |
163 | READ\ LOCK\ HELD\ ON\ ENTRY:| | |
164 | WRITE\ LOCK\ HELD\ ON\ ENTRY:| | |
165 | LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:| | |
166 | READ\ LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:| | |
167 | WRITE\ LOCK\ ACQUIRED\ AS\ SIDE\ EFFECT:| | |
168 | LOCK\ RELEASED\ AS\ SIDE\ EFFECT:| | |
169 | LOCK\ UPGRADED\ AS\ SIDE\ EFFECT:| | |
170 | LOCK\ DOWNGRADED\ AS\ SIDE\ EFFECT:| | |
171 | FUNCTIONS\ CALLED\ THROUGH\ POINTER| | |
172 | FUNCTIONS\ CALLED\ THROUGH\ MEMBER| | |
173 | LOCK\ ORDER: | |
174 | )/x; | |
175 | ||
176 | my $err_stat = 0; # exit status | |
177 | ||
178 | if ($#ARGV >= 0) { | |
179 | foreach my $arg (@ARGV) { | |
180 | my $fh = new IO::File $arg, "r"; | |
181 | if (!defined($fh)) { | |
182 | printf "%s: can not open\n", $arg; | |
183 | } else { | |
184 | &cstyle($arg, $fh); | |
185 | close $fh; | |
186 | } | |
187 | } | |
188 | } else { | |
189 | &cstyle("<stdin>", *STDIN); | |
190 | } | |
191 | exit $err_stat; | |
192 | ||
193 | my $no_errs = 0; # set for CSTYLED-protected lines | |
194 | ||
195 | sub err($) { | |
196 | my ($error) = @_; | |
197 | unless ($no_errs) { | |
f866a4ea BB |
198 | if ($verbose) { |
199 | printf $fmt, $filename, $., $error, $line; | |
200 | } else { | |
201 | printf $fmt, $filename, $., $error; | |
42d4a8e5 GM |
202 | } |
203 | if ($github_workflow) { | |
204 | printf "::error file=%s,line=%s::%s\n", $filename, $., $error; | |
205 | } | |
a35beedf BB |
206 | $err_stat = 1; |
207 | } | |
208 | } | |
209 | ||
210 | sub err_prefix($$) { | |
211 | my ($prevline, $error) = @_; | |
212 | my $out = $prevline."\n".$line; | |
213 | unless ($no_errs) { | |
f866a4ea BB |
214 | if ($verbose) { |
215 | printf $fmt, $filename, $., $error, $out; | |
216 | } else { | |
217 | printf $fmt, $filename, $., $error; | |
218 | } | |
a35beedf BB |
219 | $err_stat = 1; |
220 | } | |
221 | } | |
222 | ||
223 | sub err_prev($) { | |
224 | my ($error) = @_; | |
225 | unless ($no_errs) { | |
f866a4ea BB |
226 | if ($verbose) { |
227 | printf $fmt, $filename, $. - 1, $error, $prev; | |
228 | } else { | |
229 | printf $fmt, $filename, $. - 1, $error; | |
230 | } | |
a35beedf BB |
231 | $err_stat = 1; |
232 | } | |
233 | } | |
234 | ||
235 | sub cstyle($$) { | |
236 | ||
237 | my ($fn, $filehandle) = @_; | |
238 | $filename = $fn; # share it globally | |
239 | ||
240 | my $in_cpp = 0; | |
241 | my $next_in_cpp = 0; | |
242 | ||
243 | my $in_comment = 0; | |
244 | my $in_header_comment = 0; | |
245 | my $comment_done = 0; | |
246 | my $in_warlock_comment = 0; | |
247 | my $in_function = 0; | |
248 | my $in_function_header = 0; | |
ec441a9c | 249 | my $function_header_full_indent = 0; |
a35beedf BB |
250 | my $in_declaration = 0; |
251 | my $note_level = 0; | |
252 | my $nextok = 0; | |
253 | my $nocheck = 0; | |
254 | ||
255 | my $in_string = 0; | |
256 | ||
257 | my ($okmsg, $comment_prefix); | |
258 | ||
259 | $line = ''; | |
260 | $prev = ''; | |
261 | reset_indent(); | |
262 | ||
263 | line: while (<$filehandle>) { | |
264 | s/\r?\n$//; # strip return and newline | |
265 | ||
266 | # save the original line, then remove all text from within | |
267 | # double or single quotes, we do not want to check such text. | |
268 | ||
269 | $line = $_; | |
270 | ||
271 | # | |
272 | # C allows strings to be continued with a backslash at the end of | |
273 | # the line. We translate that into a quoted string on the previous | |
274 | # line followed by an initial quote on the next line. | |
275 | # | |
276 | # (we assume that no-one will use backslash-continuation with character | |
277 | # constants) | |
278 | # | |
279 | $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); | |
280 | ||
281 | # | |
282 | # normal strings and characters | |
283 | # | |
284 | s/'([^\\']|\\[^xX0]|\\0[0-9]*|\\[xX][0-9a-fA-F]*)'/''/g; | |
285 | s/"([^\\"]|\\.)*"/\"\"/g; | |
286 | ||
287 | # | |
288 | # detect string continuation | |
289 | # | |
290 | if ($nocheck || $in_comment) { | |
291 | $in_string = 0; | |
292 | } else { | |
293 | # | |
294 | # Now that all full strings are replaced with "", we check | |
295 | # for unfinished strings continuing onto the next line. | |
296 | # | |
297 | $in_string = | |
298 | (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || | |
299 | s/^("")*"([^\\"]|\\.)*\\$/""/); | |
300 | } | |
301 | ||
302 | # | |
303 | # figure out if we are in a cpp directive | |
304 | # | |
305 | $in_cpp = $next_in_cpp || /^\s*#/; # continued or started | |
306 | $next_in_cpp = $in_cpp && /\\$/; # only if continued | |
307 | ||
308 | # strip off trailing backslashes, which appear in long macros | |
309 | s/\s*\\$//; | |
310 | ||
311 | # an /* END CSTYLED */ comment ends a no-check block. | |
312 | if ($nocheck) { | |
313 | if (/\/\* *END *CSTYLED *\*\//) { | |
314 | $nocheck = 0; | |
315 | } else { | |
316 | reset_indent(); | |
317 | next line; | |
318 | } | |
319 | } | |
320 | ||
321 | # a /*CSTYLED*/ comment indicates that the next line is ok. | |
322 | if ($nextok) { | |
323 | if ($okmsg) { | |
324 | err($okmsg); | |
325 | } | |
326 | $nextok = 0; | |
327 | $okmsg = 0; | |
328 | if (/\/\* *CSTYLED.*\*\//) { | |
329 | /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; | |
330 | $okmsg = $1; | |
331 | $nextok = 1; | |
332 | } | |
333 | $no_errs = 1; | |
334 | } elsif ($no_errs) { | |
335 | $no_errs = 0; | |
336 | } | |
337 | ||
338 | # check length of line. | |
339 | # first, a quick check to see if there is any chance of being too long. | |
340 | if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) { | |
341 | # yes, there is a chance. | |
342 | # replace tabs with spaces and check again. | |
343 | my $eline = $line; | |
344 | 1 while $eline =~ | |
345 | s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e; | |
346 | if (length($eline) > 80) { | |
347 | err("line > 80 characters"); | |
348 | } | |
349 | } | |
350 | ||
351 | # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). | |
352 | if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE | |
353 | s/[^()]//g; # eliminate all non-parens | |
354 | $note_level += s/\(//g - length; # update paren nest level | |
355 | next; | |
356 | } | |
357 | ||
358 | # a /* BEGIN CSTYLED */ comment starts a no-check block. | |
359 | if (/\/\* *BEGIN *CSTYLED *\*\//) { | |
360 | $nocheck = 1; | |
361 | } | |
362 | ||
363 | # a /*CSTYLED*/ comment indicates that the next line is ok. | |
364 | if (/\/\* *CSTYLED.*\*\//) { | |
365 | /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; | |
366 | $okmsg = $1; | |
367 | $nextok = 1; | |
368 | } | |
369 | if (/\/\/ *CSTYLED/) { | |
370 | /^.*\/\/ *CSTYLED *(.*)$/; | |
371 | $okmsg = $1; | |
372 | $nextok = 1; | |
373 | } | |
374 | ||
375 | # universal checks; apply to everything | |
376 | if (/\t +\t/) { | |
377 | err("spaces between tabs"); | |
378 | } | |
379 | if (/ \t+ /) { | |
380 | err("tabs between spaces"); | |
381 | } | |
382 | if (/\s$/) { | |
383 | err("space or tab at end of line"); | |
384 | } | |
385 | if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { | |
386 | err("comment preceded by non-blank"); | |
387 | } | |
15b98249 AZ |
388 | if (/ARGSUSED/) { |
389 | err("ARGSUSED directive"); | |
390 | } | |
a35beedf BB |
391 | |
392 | # is this the beginning or ending of a function? | |
393 | # (not if "struct foo\n{\n") | |
2e7c1bb3 | 394 | if (/^\{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { |
a35beedf BB |
395 | $in_function = 1; |
396 | $in_declaration = 1; | |
397 | $in_function_header = 0; | |
ec441a9c | 398 | $function_header_full_indent = 0; |
a35beedf BB |
399 | $prev = $line; |
400 | next line; | |
401 | } | |
2e7c1bb3 | 402 | if (/^\}\s*(\/\*.*\*\/\s*)*$/) { |
a35beedf BB |
403 | if ($prev =~ /^\s*return\s*;/) { |
404 | err_prev("unneeded return at end of function"); | |
405 | } | |
406 | $in_function = 0; | |
407 | reset_indent(); # we don't check between functions | |
408 | $prev = $line; | |
409 | next line; | |
410 | } | |
d96e5439 | 411 | if ($in_function_header && ! /^ (\w|\.)/ ) { |
2e7c1bb3 | 412 | if (/^\{\}$/ # empty functions |
ec441a9c PD |
413 | || /;/ #run function with multiline arguments |
414 | || /#/ #preprocessor commands | |
415 | || /^[^\s\\]*\(.*\)$/ #functions without ; at the end | |
416 | || /^$/ #function declaration can't have empty line | |
417 | ) { | |
418 | $in_function_header = 0; | |
419 | $function_header_full_indent = 0; | |
420 | } elsif ($prev =~ /^__attribute__/) { #__attribute__((*)) | |
421 | $in_function_header = 0; | |
422 | $function_header_full_indent = 0; | |
423 | $prev = $line; | |
424 | next line; | |
425 | } elsif ($picky && ! (/^\t/ && $function_header_full_indent != 0)) { | |
42d4a8e5 | 426 | |
ec441a9c PD |
427 | err("continuation line should be indented by 4 spaces"); |
428 | } | |
429 | } | |
430 | ||
431 | # | |
432 | # If this matches something of form "foo(", it's probably a function | |
433 | # definition, unless it ends with ") bar;", in which case it's a declaration | |
434 | # that uses a macro to generate the type. | |
435 | # | |
436 | if (/^\w+\(/ && !/\) \w+;/) { | |
a35beedf | 437 | $in_function_header = 1; |
ec441a9c PD |
438 | if (/\($/) { |
439 | $function_header_full_indent = 1; | |
440 | } | |
441 | } | |
2e7c1bb3 | 442 | if ($in_function_header && /^\{$/) { |
ec441a9c PD |
443 | $in_function_header = 0; |
444 | $function_header_full_indent = 0; | |
445 | $in_function = 1; | |
446 | } | |
447 | if ($in_function_header && /\);$/) { | |
448 | $in_function_header = 0; | |
449 | $function_header_full_indent = 0; | |
450 | } | |
2e7c1bb3 | 451 | if ($in_function_header && /\{$/ ) { |
d4e00407 | 452 | if ($picky) { |
ec441a9c PD |
453 | err("opening brace on same line as function header"); |
454 | } | |
455 | $in_function_header = 0; | |
456 | $function_header_full_indent = 0; | |
457 | $in_function = 1; | |
458 | next line; | |
a35beedf BB |
459 | } |
460 | ||
461 | if ($in_warlock_comment && /\*\//) { | |
462 | $in_warlock_comment = 0; | |
463 | $prev = $line; | |
464 | next line; | |
465 | } | |
466 | ||
467 | # a blank line terminates the declarations within a function. | |
468 | # XXX - but still a problem in sub-blocks. | |
469 | if ($in_declaration && /^$/) { | |
470 | $in_declaration = 0; | |
471 | } | |
472 | ||
473 | if ($comment_done) { | |
474 | $in_comment = 0; | |
475 | $in_header_comment = 0; | |
476 | $comment_done = 0; | |
477 | } | |
478 | # does this looks like the start of a block comment? | |
479 | if (/$hdr_comment_start/) { | |
480 | if (!/^\t*\/\*/) { | |
481 | err("block comment not indented by tabs"); | |
482 | } | |
483 | $in_comment = 1; | |
484 | /^(\s*)\//; | |
485 | $comment_prefix = $1; | |
486 | if ($comment_prefix eq "") { | |
487 | $in_header_comment = 1; | |
488 | } | |
489 | $prev = $line; | |
490 | next line; | |
491 | } | |
492 | # are we still in the block comment? | |
493 | if ($in_comment) { | |
494 | if (/^$comment_prefix \*\/$/) { | |
495 | $comment_done = 1; | |
496 | } elsif (/\*\//) { | |
497 | $comment_done = 1; | |
498 | err("improper block comment close") | |
499 | unless ($ignore_hdr_comment && $in_header_comment); | |
500 | } elsif (!/^$comment_prefix \*[ \t]/ && | |
501 | !/^$comment_prefix \*$/) { | |
502 | err("improper block comment") | |
503 | unless ($ignore_hdr_comment && $in_header_comment); | |
504 | } | |
505 | } | |
506 | ||
507 | if ($in_header_comment && $ignore_hdr_comment) { | |
508 | $prev = $line; | |
509 | next line; | |
510 | } | |
511 | ||
512 | # check for errors that might occur in comments and in code. | |
513 | ||
8ffef572 BB |
514 | # allow spaces to be used to draw pictures in all comments. |
515 | if (/[^ ] / && !/".* .*"/ && !$in_comment) { | |
a35beedf BB |
516 | err("spaces instead of tabs"); |
517 | } | |
518 | if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && | |
d96e5439 | 519 | (!/^ (\w|\.)/ || $in_function != 0)) { |
a35beedf BB |
520 | err("indent by spaces instead of tabs"); |
521 | } | |
522 | if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { | |
523 | err("continuation line not indented by 4 spaces"); | |
524 | } | |
525 | if (/$warlock_re/ && !/\*\//) { | |
526 | $in_warlock_comment = 1; | |
527 | $prev = $line; | |
528 | next line; | |
529 | } | |
530 | if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { | |
531 | err("improper first line of block comment"); | |
532 | } | |
533 | ||
534 | if ($in_comment) { # still in comment, don't do further checks | |
535 | $prev = $line; | |
536 | next line; | |
537 | } | |
538 | ||
539 | if ((/[^(]\/\*\S/ || /^\/\*\S/) && | |
540 | !(/$lint_re/ || ($splint_comments && /$splint_re/))) { | |
541 | err("missing blank after open comment"); | |
542 | } | |
543 | if (/\S\*\/[^)]|\S\*\/$/ && | |
544 | !(/$lint_re/ || ($splint_comments && /$splint_re/))) { | |
545 | err("missing blank before close comment"); | |
546 | } | |
547 | if (/\/\/\S/) { # C++ comments | |
548 | err("missing blank after start comment"); | |
549 | } | |
550 | # check for unterminated single line comments, but allow them when | |
551 | # they are used to comment out the argument list of a function | |
552 | # declaration. | |
553 | if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) { | |
554 | err("unterminated single line comment"); | |
555 | } | |
556 | ||
557 | if (/^(#else|#endif|#include)(.*)$/) { | |
558 | $prev = $line; | |
559 | if ($picky) { | |
560 | my $directive = $1; | |
561 | my $clause = $2; | |
562 | # Enforce ANSI rules for #else and #endif: no noncomment | |
563 | # identifiers are allowed after #endif or #else. Allow | |
564 | # C++ comments since they seem to be a fact of life. | |
565 | if ((($1 eq "#endif") || ($1 eq "#else")) && | |
566 | ($clause ne "") && | |
567 | (!($clause =~ /^\s+\/\*.*\*\/$/)) && | |
568 | (!($clause =~ /^\s+\/\/.*$/))) { | |
569 | err("non-comment text following " . | |
570 | "$directive (or malformed $directive " . | |
571 | "directive)"); | |
572 | } | |
573 | } | |
574 | next line; | |
575 | } | |
576 | ||
577 | # | |
578 | # delete any comments and check everything else. Note that | |
579 | # ".*?" is a non-greedy match, so that we don't get confused by | |
580 | # multiple comments on the same line. | |
581 | # | |
582 | s/\/\*.*?\*\//\ 1/g; | |
583 | s/\/\/.*$/\ 1/; # C++ comments | |
584 | ||
585 | # delete any trailing whitespace; we have already checked for that. | |
586 | s/\s*$//; | |
587 | ||
588 | # following checks do not apply to text in comments. | |
589 | ||
590 | if (/[^<>\s][!<>=]=/ || /[^<>][!<>=]=[^\s,]/ || | |
591 | (/[^->]>[^,=>\s]/ && !/[^->]>$/) || | |
592 | (/[^<]<[^,=<\s]/ && !/[^<]<$/) || | |
593 | /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { | |
594 | err("missing space around relational operator"); | |
595 | } | |
596 | if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || | |
597 | (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || | |
598 | (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { | |
599 | # XXX - should only check this for C++ code | |
600 | # XXX - there are probably other forms that should be allowed | |
601 | if (!/\soperator=/) { | |
602 | err("missing space around assignment operator"); | |
603 | } | |
604 | } | |
605 | if (/[,;]\S/ && !/\bfor \(;;\)/) { | |
606 | err("comma or semicolon followed by non-blank"); | |
607 | } | |
608 | # allow "for" statements to have empty "while" clauses | |
609 | if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { | |
610 | err("comma or semicolon preceded by blank"); | |
611 | } | |
612 | if (/^\s*(&&|\|\|)/) { | |
613 | err("improper boolean continuation"); | |
614 | } | |
615 | if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { | |
616 | err("more than one space around boolean operator"); | |
617 | } | |
618 | if (/\b(for|if|while|switch|sizeof|return|case)\(/) { | |
619 | err("missing space between keyword and paren"); | |
620 | } | |
621 | if (/(\b(for|if|while|switch|return)\b.*){2,}/ && !/^#define/) { | |
622 | # multiple "case" and "sizeof" allowed | |
623 | err("more than one keyword on line"); | |
624 | } | |
625 | if (/\b(for|if|while|switch|sizeof|return|case)\s\s+\(/ && | |
626 | !/^#if\s+\(/) { | |
627 | err("extra space between keyword and paren"); | |
628 | } | |
629 | # try to detect "func (x)" but not "if (x)" or | |
630 | # "#define foo (x)" or "int (*func)();" | |
631 | if (/\w\s\(/) { | |
632 | my $s = $_; | |
633 | # strip off all keywords on the line | |
634 | s/\b(for|if|while|switch|return|case|sizeof)\s\(/XXX(/g; | |
635 | s/#elif\s\(/XXX(/g; | |
636 | s/^#define\s+\w+\s+\(/XXX(/; | |
637 | # do not match things like "void (*f)();" | |
638 | # or "typedef void (func_t)();" | |
639 | s/\w\s\(+\*/XXX(*/g; | |
640 | s/\b($typename|void)\s+\(+/XXX(/og; | |
641 | if (/\w\s\(/) { | |
642 | err("extra space between function name and left paren"); | |
643 | } | |
644 | $_ = $s; | |
645 | } | |
646 | # try to detect "int foo(x)", but not "extern int foo(x);" | |
647 | # XXX - this still trips over too many legitimate things, | |
648 | # like "int foo(x,\n\ty);" | |
649 | # if (/^(\w+(\s|\*)+)+\w+\(/ && !/\)[;,](\s|\ 1)*$/ && | |
650 | # !/^(extern|static)\b/) { | |
651 | # err("return type of function not on separate line"); | |
652 | # } | |
653 | # this is a close approximation | |
654 | if (/^(\w+(\s|\*)+)+\w+\(.*\)(\s|\ 1)*$/ && | |
655 | !/^(extern|static)\b/) { | |
656 | err("return type of function not on separate line"); | |
657 | } | |
658 | if (/^#define /) { | |
659 | err("#define followed by space instead of tab"); | |
660 | } | |
661 | if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) { | |
662 | err("unparenthesized return expression"); | |
663 | } | |
664 | if (/\bsizeof\b/ && !/\bsizeof\s*\(.*\)/) { | |
665 | err("unparenthesized sizeof expression"); | |
666 | } | |
667 | if (/\(\s/) { | |
668 | err("whitespace after left paren"); | |
669 | } | |
50240467 NB |
670 | # Allow "for" statements to have empty "continue" clauses. |
671 | # Allow right paren on its own line unless we're being picky (-p). | |
672 | if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/ && ($picky || !/^\s*\)/)) { | |
a35beedf BB |
673 | err("whitespace before right paren"); |
674 | } | |
675 | if (/^\s*\(void\)[^ ]/) { | |
676 | err("missing space after (void) cast"); | |
677 | } | |
f866a4ea | 678 | if (/\S\{/ && !/\{\{/) { |
a35beedf BB |
679 | err("missing space before left brace"); |
680 | } | |
2e7c1bb3 | 681 | if ($in_function && /^\s+\{/ && |
a35beedf BB |
682 | ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) { |
683 | err("left brace starting a line"); | |
684 | } | |
2e7c1bb3 | 685 | if (/\}(else|while)/) { |
a35beedf BB |
686 | err("missing space after right brace"); |
687 | } | |
2e7c1bb3 | 688 | if (/\}\s\s+(else|while)/) { |
a35beedf BB |
689 | err("extra space after right brace"); |
690 | } | |
691 | if (/\b_VOID\b|\bVOID\b|\bSTATIC\b/) { | |
692 | err("obsolete use of VOID or STATIC"); | |
693 | } | |
694 | if (/\b$typename\*/o) { | |
695 | err("missing space between type name and *"); | |
696 | } | |
697 | if (/^\s+#/) { | |
698 | err("preprocessor statement not in column 1"); | |
699 | } | |
700 | if (/^#\s/) { | |
701 | err("blank after preprocessor #"); | |
702 | } | |
703 | if (/!\s*(strcmp|strncmp|bcmp)\s*\(/) { | |
704 | err("don't use boolean ! with comparison functions"); | |
705 | } | |
706 | ||
707 | # | |
708 | # We completely ignore, for purposes of indentation: | |
709 | # * lines outside of functions | |
710 | # * preprocessor lines | |
711 | # | |
712 | if ($check_continuation && $in_function && !$in_cpp) { | |
713 | process_indent($_); | |
714 | } | |
715 | if ($picky) { | |
716 | # try to detect spaces after casts, but allow (e.g.) | |
717 | # "sizeof (int) + 1", "void (*funcptr)(int) = foo;", and | |
718 | # "int foo(int) __NORETURN;" | |
719 | if ((/^\($typename( \*+)?\)\s/o || | |
720 | /\W\($typename( \*+)?\)\s/o) && | |
721 | !/sizeof\s*\($typename( \*)?\)\s/o && | |
722 | !/\($typename( \*+)?\)\s+=[^=]/o) { | |
723 | err("space after cast"); | |
724 | } | |
725 | if (/\b$typename\s*\*\s/o && | |
726 | !/\b$typename\s*\*\s+const\b/o) { | |
727 | err("unary * followed by space"); | |
728 | } | |
729 | } | |
730 | if ($check_posix_types) { | |
731 | # try to detect old non-POSIX types. | |
732 | # POSIX requires all non-standard typedefs to end in _t, | |
733 | # but historically these have been used. | |
734 | if (/\b(unchar|ushort|uint|ulong|u_int|u_short|u_long|u_char|quad)\b/) { | |
735 | err("non-POSIX typedef $1 used: use $old2posix{$1} instead"); | |
736 | } | |
737 | } | |
738 | if ($heuristic) { | |
739 | # cannot check this everywhere due to "struct {\n...\n} foo;" | |
740 | if ($in_function && !$in_declaration && | |
2e7c1bb3 DH |
741 | /\}./ && !/\}\s+=/ && !/\{.*\}[;,]$/ && !/\}(\s|\ 1)*$/ && |
742 | !/\} (else|while)/ && !/\}\}/) { | |
a35beedf BB |
743 | err("possible bad text following right brace"); |
744 | } | |
745 | # cannot check this because sub-blocks in | |
746 | # the middle of code are ok | |
2e7c1bb3 | 747 | if ($in_function && /^\s+\{/) { |
a35beedf BB |
748 | err("possible left brace starting a line"); |
749 | } | |
750 | } | |
751 | if (/^\s*else\W/) { | |
2e7c1bb3 | 752 | if ($prev =~ /^\s*\}$/) { |
a35beedf BB |
753 | err_prefix($prev, |
754 | "else and right brace should be on same line"); | |
755 | } | |
756 | } | |
757 | $prev = $line; | |
758 | } | |
759 | ||
760 | if ($prev eq "") { | |
761 | err("last line in file is blank"); | |
762 | } | |
763 | ||
764 | } | |
765 | ||
766 | # | |
767 | # Continuation-line checking | |
768 | # | |
769 | # The rest of this file contains the code for the continuation checking | |
770 | # engine. It's a pretty simple state machine which tracks the expression | |
771 | # depth (unmatched '('s and '['s). | |
772 | # | |
773 | # Keep in mind that the argument to process_indent() has already been heavily | |
774 | # processed; all comments have been replaced by control-A, and the contents of | |
775 | # strings and character constants have been elided. | |
776 | # | |
777 | ||
778 | my $cont_in; # currently inside of a continuation | |
779 | my $cont_off; # skipping an initializer or definition | |
780 | my $cont_noerr; # suppress cascading errors | |
781 | my $cont_start; # the line being continued | |
782 | my $cont_base; # the base indentation | |
783 | my $cont_first; # this is the first line of a statement | |
784 | my $cont_multiseg; # this continuation has multiple segments | |
785 | ||
786 | my $cont_special; # this is a C statement (if, for, etc.) | |
787 | my $cont_macro; # this is a macro | |
788 | my $cont_case; # this is a multi-line case | |
789 | ||
790 | my @cont_paren; # the stack of unmatched ( and [s we've seen | |
791 | ||
792 | sub | |
793 | reset_indent() | |
794 | { | |
795 | $cont_in = 0; | |
796 | $cont_off = 0; | |
797 | } | |
798 | ||
799 | sub | |
800 | delabel($) | |
801 | { | |
802 | # | |
803 | # replace labels with tabs. Note that there may be multiple | |
804 | # labels on a line. | |
805 | # | |
806 | local $_ = $_[0]; | |
807 | ||
808 | while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) { | |
809 | my ($pre_tabs, $label, $rest) = ($1, $2, $3); | |
810 | $_ = $pre_tabs; | |
811 | while ($label =~ s/^([^\t]*)(\t+)//) { | |
812 | $_ .= "\t" x (length($2) + length($1) / 8); | |
813 | } | |
814 | $_ .= ("\t" x (length($label) / 8)).$rest; | |
815 | } | |
816 | ||
817 | return ($_); | |
818 | } | |
819 | ||
820 | sub | |
821 | process_indent($) | |
822 | { | |
823 | require strict; | |
824 | local $_ = $_[0]; # preserve the global $_ | |
825 | ||
826 | s/\ 1//g; # No comments | |
827 | s/\s+$//; # Strip trailing whitespace | |
828 | ||
829 | return if (/^$/); # skip empty lines | |
830 | ||
831 | # regexps used below; keywords taking (), macros, and continued cases | |
832 | my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b'; | |
833 | my $macro = '[A-Z_][A-Z_0-9]*\('; | |
834 | my $case = 'case\b[^:]*$'; | |
835 | ||
836 | # skip over enumerations, array definitions, initializers, etc. | |
837 | if ($cont_off <= 0 && !/^\s*$special/ && | |
2e7c1bb3 DH |
838 | (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*))\{/ || |
839 | (/^\s*\{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { | |
a35beedf BB |
840 | $cont_in = 0; |
841 | $cont_off = tr/{/{/ - tr/}/}/; | |
842 | return; | |
843 | } | |
844 | if ($cont_off) { | |
845 | $cont_off += tr/{/{/ - tr/}/}/; | |
846 | return; | |
847 | } | |
848 | ||
849 | if (!$cont_in) { | |
850 | $cont_start = $line; | |
851 | ||
852 | if (/^\t* /) { | |
853 | err("non-continuation indented 4 spaces"); | |
854 | $cont_noerr = 1; # stop reporting | |
855 | } | |
856 | $_ = delabel($_); # replace labels with tabs | |
857 | ||
858 | # check if the statement is complete | |
859 | return if (/^\s*\}?$/); | |
860 | return if (/^\s*\}?\s*else\s*\{?$/); | |
861 | return if (/^\s*do\s*\{?$/); | |
2e7c1bb3 DH |
862 | return if (/\{$/); |
863 | return if (/\}[,;]?$/); | |
a35beedf BB |
864 | |
865 | # Allow macros on their own lines | |
866 | return if (/^\s*[A-Z_][A-Z_0-9]*$/); | |
867 | ||
868 | # cases we don't deal with, generally non-kosher | |
2e7c1bb3 | 869 | if (/\{/) { |
a35beedf BB |
870 | err("stuff after {"); |
871 | return; | |
872 | } | |
873 | ||
874 | # Get the base line, and set up the state machine | |
875 | /^(\t*)/; | |
876 | $cont_base = $1; | |
877 | $cont_in = 1; | |
878 | @cont_paren = (); | |
879 | $cont_first = 1; | |
880 | $cont_multiseg = 0; | |
881 | ||
882 | # certain things need special processing | |
883 | $cont_special = /^\s*$special/? 1 : 0; | |
884 | $cont_macro = /^\s*$macro/? 1 : 0; | |
885 | $cont_case = /^\s*$case/? 1 : 0; | |
886 | } else { | |
887 | $cont_first = 0; | |
888 | ||
889 | # Strings may be pulled back to an earlier (half-)tabstop | |
890 | unless ($cont_noerr || /^$cont_base / || | |
891 | (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) { | |
892 | err_prefix($cont_start, | |
893 | "continuation should be indented 4 spaces"); | |
894 | } | |
895 | } | |
896 | ||
897 | my $rest = $_; # keeps the remainder of the line | |
898 | ||
899 | # | |
900 | # The split matches 0 characters, so that each 'special' character | |
901 | # is processed separately. Parens and brackets are pushed and | |
902 | # popped off the @cont_paren stack. For normal processing, we wait | |
903 | # until a ; or { terminates the statement. "special" processing | |
904 | # (if/for/while/switch) is allowed to stop when the stack empties, | |
905 | # as is macro processing. Case statements are terminated with a : | |
906 | # and an empty paren stack. | |
907 | # | |
908 | foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) { | |
909 | next if (length($_) == 0); | |
910 | ||
911 | # rest contains the remainder of the line | |
912 | my $rxp = "[^\Q$_\E]*\Q$_\E"; | |
913 | $rest =~ s/^$rxp//; | |
914 | ||
915 | if (/\(/ || /\[/) { | |
916 | push @cont_paren, $_; | |
917 | } elsif (/\)/ || /\]/) { | |
918 | my $cur = $_; | |
919 | tr/\)\]/\(\[/; | |
920 | ||
921 | my $old = (pop @cont_paren); | |
922 | if (!defined($old)) { | |
923 | err("unexpected '$cur'"); | |
924 | $cont_in = 0; | |
925 | last; | |
926 | } elsif ($old ne $_) { | |
927 | err("'$cur' mismatched with '$old'"); | |
928 | $cont_in = 0; | |
929 | last; | |
930 | } | |
931 | ||
932 | # | |
933 | # If the stack is now empty, do special processing | |
934 | # for if/for/while/switch and macro statements. | |
935 | # | |
936 | next if (@cont_paren != 0); | |
937 | if ($cont_special) { | |
2e7c1bb3 | 938 | if ($rest =~ /^\s*\{?$/) { |
a35beedf BB |
939 | $cont_in = 0; |
940 | last; | |
941 | } | |
942 | if ($rest =~ /^\s*;$/) { | |
943 | err("empty if/for/while body ". | |
944 | "not on its own line"); | |
945 | $cont_in = 0; | |
946 | last; | |
947 | } | |
948 | if (!$cont_first && $cont_multiseg == 1) { | |
949 | err_prefix($cont_start, | |
950 | "multiple statements continued ". | |
951 | "over multiple lines"); | |
952 | $cont_multiseg = 2; | |
953 | } elsif ($cont_multiseg == 0) { | |
954 | $cont_multiseg = 1; | |
955 | } | |
956 | # We've finished this section, start | |
957 | # processing the next. | |
958 | goto section_ended; | |
959 | } | |
960 | if ($cont_macro) { | |
961 | if ($rest =~ /^$/) { | |
962 | $cont_in = 0; | |
963 | last; | |
964 | } | |
965 | } | |
966 | } elsif (/\;/) { | |
967 | if ($cont_case) { | |
968 | err("unexpected ;"); | |
969 | } elsif (!$cont_special) { | |
970 | err("unexpected ;") if (@cont_paren != 0); | |
971 | if (!$cont_first && $cont_multiseg == 1) { | |
972 | err_prefix($cont_start, | |
973 | "multiple statements continued ". | |
974 | "over multiple lines"); | |
975 | $cont_multiseg = 2; | |
976 | } elsif ($cont_multiseg == 0) { | |
977 | $cont_multiseg = 1; | |
978 | } | |
979 | if ($rest =~ /^$/) { | |
980 | $cont_in = 0; | |
981 | last; | |
982 | } | |
983 | if ($rest =~ /^\s*special/) { | |
984 | err("if/for/while/switch not started ". | |
985 | "on its own line"); | |
986 | } | |
987 | goto section_ended; | |
988 | } | |
989 | } elsif (/\{/) { | |
990 | err("{ while in parens/brackets") if (@cont_paren != 0); | |
991 | err("stuff after {") if ($rest =~ /[^\s}]/); | |
992 | $cont_in = 0; | |
993 | last; | |
994 | } elsif (/\}/) { | |
995 | err("} while in parens/brackets") if (@cont_paren != 0); | |
996 | if (!$cont_special && $rest !~ /^\s*(while|else)\b/) { | |
997 | if ($rest =~ /^$/) { | |
998 | err("unexpected }"); | |
999 | } else { | |
1000 | err("stuff after }"); | |
1001 | } | |
1002 | $cont_in = 0; | |
1003 | last; | |
1004 | } | |
1005 | } elsif (/\:/ && $cont_case && @cont_paren == 0) { | |
1006 | err("stuff after multi-line case") if ($rest !~ /$^/); | |
1007 | $cont_in = 0; | |
1008 | last; | |
1009 | } | |
1010 | next; | |
1011 | section_ended: | |
1012 | # End of a statement or if/while/for loop. Reset | |
1013 | # cont_special and cont_macro based on the rest of the | |
1014 | # line. | |
1015 | $cont_special = ($rest =~ /^\s*$special/)? 1 : 0; | |
1016 | $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0; | |
1017 | $cont_case = 0; | |
1018 | next; | |
1019 | } | |
1020 | $cont_noerr = 0 if (!$cont_in); | |
1021 | } |