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