Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/perl -w
      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 #
     22 
     23 #
     24 # ident	"%Z%%M%	%I%	%E% SMI"
     25 #
     26 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     27 # Use is subject to license terms.
     28 #
     29 # jstyle - check for some common stylistic errors.
     30 #
     31 
     32 require 5.006;
     33 use Getopt::Std;
     34 use strict;
     35 
     36 my $usage =
     37 "usage: jstyle [-c] [-h] [-p] [-t] [-v] [-C] file ...
     38 	-c	check continuation line indenting
     39 	-h	perform heuristic checks that are sometimes wrong
     40 	-p	perform some of the more picky checks
     41 	-t	insist on indenting by tabs
     42 	-v	verbose
     43 	-C	don't check anything in header block comments
     44 ";
     45 
     46 my %opts;
     47 
     48 # Keep -s, as it's been around for a while.  It just doesn't do anything
     49 # anymore.
     50 if (!getopts("chpstvC", \%opts)) {
     51 	print $usage;
     52 	exit 2;
     53 }
     54 
     55 my $check_continuation = $opts{'c'};
     56 my $heuristic = $opts{'h'};
     57 my $picky = $opts{'p'};
     58 my $tabs = $opts{'t'};
     59 my $verbose = $opts{'v'};
     60 my $ignore_hdr_comment = $opts{'C'};
     61 
     62 my ($filename, $line, $prev);
     63 my $err_stat = 0;		# Exit status
     64 
     65 my $fmt;
     66 
     67 if ($verbose) {
     68 	$fmt = "%s: %d: %s\n%s\n";
     69 } else {
     70 	$fmt = "%s: %d: %s\n";
     71 }
     72 
     73 # Note, following must be in single quotes so that \s and \w work right.
     74 my $typename = '(int|char|boolean|byte|short|long|float|double)';
     75 my $keywords = '(for|if|while|switch|return|catch|synchronized|throw|assert)';
     76 # See perlre(1) for the meaning of (??{ ... })
     77 my $annotations = ""; $annotations = qr/@\w+\((?:(?>[^()]+)|(??{ $annotations }))*\)/;
     78 my $generics = ""; $generics = qr/<(([\s\w,.?[\]]| & )+|(??{ $generics }))*>/;
     79 my $relationalops = qr/>=|<=|<|>|!=|==/;
     80 my $shiftops = qr/<<<|>>>|<<|>>/;
     81 my $shiftassignmentops = qr/[<>]{2,3}=/;
     82 my $assignmentops = qr/[-+\/*|&^%]?=/;
     83 # These need to be in decreasing order of length
     84 my $allops = qr/$shiftassignmentops|$shiftops|$relationalops|$assignmentops/;
     85 
     86 if ($#ARGV >= 0) {
     87 	foreach my $arg (@ARGV) {
     88 		if (!open(STDIN, $arg)) {
     89 			printf "%s: can not open\n", $arg;
     90 		} else {
     91 			&jstyle($arg);
     92 			close STDIN;
     93 		}
     94 	}
     95 } else {
     96 	&jstyle("<stdin>");
     97 }
     98 exit $err_stat;
     99 
    100 sub err($) {
    101 	printf $fmt, $filename, $., $_[0], $line;
    102 	$err_stat = 1;
    103 }
    104 
    105 sub jstyle($) {
    106 
    107 my $in_comment = 0;
    108 my $in_header_comment = 0;
    109 my $in_continuation = 0;
    110 my $in_class = 0;
    111 my $in_declaration = 0;
    112 my $nextok = 0;
    113 my $nocheck = 0;
    114 my $expect_continuation = 0;
    115 my $continuation_indent;
    116 my $okmsg;
    117 my $comment_prefix;
    118 my $comment_done;
    119 my $cpp_comment;
    120 
    121 $filename = $_[0];
    122 
    123 line: while (<STDIN>) {
    124 	s/\r?\n$//;	# strip return and newline
    125 
    126 	# save the original line, then remove all text from within
    127 	# double or single quotes, we do not want to check such text.
    128 
    129 	$line = $_;
    130 	s/"[^"]*"/\"\"/g;
    131 	s/'.'/''/g;
    132 
    133 	# an /* END JSTYLED */ comment ends a no-check block.
    134 	if ($nocheck) {
    135 		if (/\/\* *END *JSTYLED *\*\//) {
    136 			$nocheck = 0;
    137 		} else {
    138 			next line;
    139 		}
    140 	}
    141 
    142 	# a /*JSTYLED*/ comment indicates that the next line is ok.
    143 	if ($nextok) {
    144 		if ($okmsg) {
    145 			err($okmsg);
    146 		}
    147 		$nextok = 0;
    148 		$okmsg = 0;
    149 		if (/\/\* *JSTYLED.*\*\//) {
    150 			/^.*\/\* *JSTYLED *(.*) *\*\/.*$/;
    151 			$okmsg = $1;
    152 			$nextok = 1;
    153 		}
    154 		$prev = $line;
    155 		next line;
    156 	}
    157 
    158 	# remember whether we expect to be inside a continuation line.
    159 	$in_continuation = $expect_continuation;
    160 
    161 	# check for proper continuation line.  blank lines
    162 	# in the middle of the
    163 	# continuation do not count.
    164 	# XXX - only check within functions.
    165 	if ($check_continuation && $expect_continuation && $in_class &&
    166 	    !/^\s*$/) {
    167 		# continuation line must start with whitespace of
    168 		# previous line, plus either 4 spaces or a tab, but
    169 		# do not check lines that start with a string constant
    170 		# since they are often shifted to the left to make them
    171 		# fit on the line.
    172 		if (!/^$continuation_indent    \S/ &&
    173 		    !/^$continuation_indent\t\S/ && !/^\s*"/) {
    174 			err("continuation line improperly indented");
    175 		}
    176 		$expect_continuation = 0;
    177 	}
    178 
    179 	# a /* BEGIN JSTYLED */ comment starts a no-check block.
    180 	if (/\/\* *BEGIN *JSTYLED *\*\//) {
    181 		$nocheck = 1;
    182 	}
    183 
    184 	# a /*JSTYLED*/ comment indicates that the next line is ok.
    185 	if (/\/\* *JSTYLED.*\*\//) {
    186 		/^.*\/\* *JSTYLED *(.*) *\*\/.*$/;
    187 		$okmsg = $1;
    188 		$nextok = 1;
    189 	}
    190 	if (/\/\/ *JSTYLED/) {
    191 		/^.*\/\/ *JSTYLED *(.*)$/;
    192 		$okmsg = $1;
    193 		$nextok = 1;
    194 	}
    195 
    196 	# is this the beginning or ending of a class?
    197 	if (/^(public\s+)*\w(class|interface)\s/) {
    198 		$in_class = 1;
    199 		$in_declaration = 1;
    200 		$prev = $line;
    201 		next line;
    202 	}
    203 	if (/^}\s*(\/\*.*\*\/\s*)*$/) {
    204 		$in_class = 0;
    205 		$prev = $line;
    206 		next line;
    207 	}
    208 
    209 	if ($comment_done) {
    210 		$in_comment = 0;
    211 		$in_header_comment = 0;
    212 		$comment_done = 0;
    213 	}
    214 	# does this looks like the start of a block comment?
    215 	if (/^\s*\/\*/ && !/^\s*\/\*.*\*\//) {
    216 		if (/^\s*\/\*./ && !/^\s*\/\*\*$/) {
    217 			err("improper first line of block comment");
    218 		}
    219 		if (!/^(\t|    )*\/\*/) {
    220 			err("block comment not indented properly");
    221 		}
    222 		$in_comment = 1;
    223 		/^(\s*)\//;
    224 		$comment_prefix = $1;
    225 		if ($comment_prefix eq "") {
    226 			$in_header_comment = 1;
    227 		}
    228 		$prev = $line;
    229 		next line;
    230 	}
    231 	# are we still in the block comment?
    232 	if ($in_comment) {
    233 		if (/^$comment_prefix \*\/$/) {
    234 			$comment_done = 1;
    235 		} elsif (/\*\//) {
    236 			$comment_done = 1;
    237 			err("improper block comment close")
    238 			    unless ($ignore_hdr_comment && $in_header_comment);
    239 		} elsif (!/^$comment_prefix \*[ \t]/ &&
    240 		    !/^$comment_prefix \*$/) {
    241 			err("improper block comment")
    242 			    unless ($ignore_hdr_comment && $in_header_comment);
    243 		}
    244 	}
    245 
    246 	if ($in_header_comment && $ignore_hdr_comment) {
    247 		$prev = $line;
    248 		next line;
    249 	}
    250 
    251 	# check for errors that might occur in comments and in code.
    252 
    253 	# check length of line.
    254 	# first, a quick check to see if there is any chance of being too long.
    255 	if ($line =~ tr/\t/\t/ * 7 + length($line) > 80) {
    256 		# yes, there is a chance.
    257 		# replace tabs with spaces and check again.
    258 		my $eline = $line;
    259 		1 while $eline =~
    260 		    s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
    261 		if (length($eline) > 80) {
    262 			err("line > 80 characters");
    263 		}
    264 	}
    265 
    266 	# Allow spaces to be used to draw pictures in header comments, but
    267 	# disallow blocks of spaces almost everywhere else.  In particular,
    268 	# five spaces are also allowed at the end of a line's indentation
    269 	# if the rest of the line belongs to a block comment.
    270 	if (!$in_header_comment &&
    271 	    /[^ ]     / &&
    272 	    !(/^\t*     \*/ && !/^\t*     \*.*     /)) {
    273 		err("spaces instead of tabs");
    274 	}
    275 	if ($tabs && /^ / && !/^ \*[ \t\/]/ && !/^ \*$/ &&
    276 	    (!/^    \w/ || $in_class != 0)) {
    277 		err("indent by spaces instead of tabs");
    278 	}
    279 	if (!$in_comment && (/^(\t    )* {1,3}\S/ || /^(\t    )* {5,7}\S/) &&
    280 	    !(/^\s*[-+|&\/?:=]/ || ($prev =~ /,\s*$/))) {
    281 		err("indent not a multiple of 4");
    282 	}
    283 	if (/\s$/) {
    284 		err("space or tab at end of line");
    285 	}
    286 if (0) {
    287 	if (/^[\t]+ [^ \t\*]/ || /^[\t]+  \S/ || /^[\t]+   \S/) {
    288 		err("continuation line not indented by 4 spaces");
    289 	}
    290 }
    291 	if (/\/\//) {
    292 		$cpp_comment = 1;
    293 	}
    294 	if (!$cpp_comment && /[^ \t(\/]\/\*/ && !/\w\(\/\*.*\*\/\);/) {
    295 		err("comment preceded by non-blank");
    296 	}
    297 	if (/\t +\t/) {
    298 		err("spaces between tabs");
    299 	}
    300 	if (/ \t+ /) {
    301 		err("tabs between spaces");
    302 	}
    303 
    304 	if ($in_comment) {	# still in comment
    305 		$prev = $line;
    306 		next line;
    307 	}
    308 
    309 	if (!$cpp_comment && ((/\/\*\S/ && !/\/\*\*/) || /\/\*\*\S/)) {
    310 		err("missing blank after open comment");
    311 	}
    312 	if (!$cpp_comment && /\S\*\//) {
    313 		err("missing blank before close comment");
    314 	}
    315 	# check for unterminated single line comments.
    316 	if (/\S.*\/\*/ && !/\S.*\/\*.*\*\//) {
    317 		err("unterminated single line comment");
    318 	}
    319 
    320 	# delete any comments and check everything else.  Be sure to leave
    321 	# //-style comments intact, and if there are multiple comments on a
    322 	# line, preserve whatever's in between.
    323 	s/(?<!\/)\/\*.*?\*\///g;
    324 	# Check for //-style comments only outside of block comments
    325 	if (m{(//(?!$))} && substr($_, $+[0], 1) !~ /[ \t]/) {
    326 		err("missing blank after start comment");
    327 	}
    328 	s/\/\/.*$//;		# C++ comments
    329 	$cpp_comment = 0;
    330 
    331 	# delete any trailing whitespace; we have already checked for that.
    332 	s/\s*$//;
    333 
    334 	# We don't style (yet) what's inside annotations, so just delete them.
    335 	s/$annotations//;
    336 
    337 	# following checks do not apply to text in comments.
    338 
    339 	# if it looks like an operator at the end of the line, and it is
    340 	# not really the end of a comment (...*/), and it is not really
    341 	# a label (done:), and it is not a case label (case FOO:),
    342 	# or we are not in a function definition (ANSI C style) and the
    343 	# operator is a "," (to avoid hitting "int\nfoo(\n\tint i,\n\tint j)"),
    344 	# or we are in a function and the operator is a
    345 	# "*" (to avoid hitting on "char*\nfunc()").
    346 	if ((/[-+|&\/?:=]$/ && !/\*\/$/ && !/^\s*\w*:$/ &&
    347 	    !/^\s\s*case\s\s*\w*:$/) ||
    348 	    /,$/ ||
    349 	    ($in_class && /\*$/)) {
    350 		$expect_continuation = 1;
    351 		if (!$in_continuation) {
    352 			/^(\s*)\S/;
    353 			$continuation_indent = $1;
    354 		}
    355 	}
    356 	while (/($allops)/g) {
    357 		my $z = substr($_, $-[1] - 1);
    358 		if ($z !~ /\s\Q$1\E(?:\s|$)/) {
    359 			my $m = $1;
    360 			my $shift;
    361 			# @+ is available only in the currently active
    362 			# dynamic scope.  Assign it to a new variable
    363 			# to pass it into the if block.
    364 			if ($z =~ /($generics)/ &&
    365 			    ($shift = $+[1])) {
    366 				pos $_ += $shift;
    367 				next;
    368 			}
    369 
    370 			# These need to be in decreasing order of length
    371 			# (violable as long as there's no ambiguity)
    372 			my $nospace = "missing space around";
    373 			if ($m =~ $shiftassignmentops) {
    374 				err("$nospace assignment operator");
    375 			} elsif ($m =~ $shiftops) {
    376 				err("$nospace shift operator");
    377 			} elsif ($m =~ $relationalops) {
    378 				err("$nospace relational operator");
    379 			} elsif ($m =~ $assignmentops) {
    380 				err("$nospace assignment operator");
    381 			}
    382 		}
    383 	}
    384 	if (/[,;]\S/ && !/\bfor \(;;\)/) {
    385 		err("comma or semicolon followed by non-blank");
    386 	}
    387 	# allow "for" statements to have empty "while" clauses
    388 	if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) {
    389 		err("comma or semicolon preceded by blank");
    390 	}
    391 if (0) {
    392 	if (/^\s*(&&|\|\|)/) {
    393 		err("improper boolean continuation");
    394 	}
    395 }
    396 	if ($picky && /\S   *(&&|\|\|)/ || /(&&|\|\|)   *\S/) {
    397 		err("more than one space around boolean operator");
    398 	}
    399 	if (/\b$keywords\(/) {
    400 		err("missing space between keyword and paren");
    401 	}
    402 	if (/(\b$keywords\b.*){2,}/ && !/\bcase\b.*/) { # "case" excepted
    403 		err("more than one keyword on line");
    404 	}
    405 	if (/\b$keywords\s\s+\(/ &&
    406 	    !/^#if\s+\(/) {
    407 		err("extra space between keyword and paren");
    408 	}
    409 	# try to detect "func (x)" but not "if (x)" or
    410 	# "int (*func)();"
    411 	if (/\w\s\(/) {
    412 		my $save = $_;
    413 		# strip off all keywords on the line
    414 		s/\b$keywords\s\(/XXX(/g;
    415 		#s/\b($typename|void)\s+\(+/XXX(/og;
    416 		if (/\w\s\(/) {
    417 			err("extra space between function name and left paren");
    418 		}
    419 		$_ = $save;
    420 	}
    421 	if (/\(\s/) {
    422 		err("whitespace after left paren");
    423 	}
    424 	# allow "for" statements to have empty "continue" clauses
    425 	if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) {
    426 		err("whitespace before right paren");
    427 	}
    428 	if (/^\s*\(void\)[^ ]/) {
    429 		err("missing space after (void) cast");
    430 	}
    431 	if (/\S{/ && !/{{/) {
    432 		err("missing space before left brace");
    433 	}
    434 	if ($in_class && /^\s+{/ && ($prev =~ /\)\s*$/)) {
    435 		err("left brace starting a line");
    436 	}
    437 	if (/}(else|while)/) {
    438 		err("missing space after right brace");
    439 	}
    440 	if (/}\s\s+(else|while)/) {
    441 		err("extra space after right brace");
    442 	}
    443 	if (/\b$typename\*/o) {
    444 		err("missing space between type name and *");
    445 	}
    446 	if ($heuristic) {
    447 		# cannot check this everywhere due to "struct {\n...\n} foo;"
    448 		if ($in_class && !$in_declaration &&
    449 		    /}./ && !/}\s+=/ && !/{.*}[;,]$/ && !/}(\s|)*$/ &&
    450 		    !/} (else|while)/ && !/}}/) {
    451 			err("possible bad text following right brace");
    452 		}
    453 		# cannot check this because sub-blocks in
    454 		# the middle of code are ok
    455 		if ($in_class && /^\s+{/) {
    456 			err("possible left brace starting a line");
    457 		}
    458 	}
    459 	if (/^\s*else\W/) {
    460 		if ($prev =~ /^\s*}$/) {
    461 			my $str = "else and right brace should be on same line";
    462 			printf $fmt, $filename, $., $str, $prev;
    463 			if ($verbose) {
    464 				printf "%s\n", $line;
    465 			}
    466 		}
    467 	}
    468 	$prev = $line;
    469 }
    470 
    471 if ($picky && $prev eq "") {
    472 	err("last line in file is blank");
    473 }
    474 
    475 }
    476