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 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 2; 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); 63my $err_stat = 0; # Exit status 64 65my $fmt; 66 67if ($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. 74my $typename = '(int|char|boolean|byte|short|long|float|double)'; 75my $keywords = '(for|if|while|switch|return|catch|synchronized|throw|assert)'; 76# See perlre(1) for the meaning of (??{ ... }) 77my $annotations = ""; $annotations = qr/@\w+\((?:(?>[^()]+)|(??{ $annotations }))*\)/; 78my $generics = ""; $generics = qr/<(([\s\w,.?[\]]| & )+|(??{ $generics }))*>/; 79my $relationalops = qr/>=|<=|<|>|!=|==/; 80my $shiftops = qr/<<<|>>>|<<|>>/; 81my $shiftassignmentops = qr/[<>]{2,3}=/; 82my $assignmentops = qr/[-+\/*|&^%]?=/; 83# These need to be in decreasing order of length 84my $allops = qr/$shiftassignmentops|$shiftops|$relationalops|$assignmentops/; 85 86if ($#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} 98exit $err_stat; 99 100sub err($) { 101 printf $fmt, $filename, $., $_[0], $line; 102 $err_stat = 1; 103} 104 105sub jstyle($) { 106 107my $in_comment = 0; 108my $in_header_comment = 0; 109my $in_continuation = 0; 110my $in_class = 0; 111my $in_declaration = 0; 112my $nextok = 0; 113my $nocheck = 0; 114my $expect_continuation = 0; 115my $continuation_indent; 116my $okmsg; 117my $comment_prefix; 118my $comment_done; 119my $cpp_comment; 120 121$filename = $_[0]; 122 123line: 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 } 286if (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 } 391if (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 471if ($picky && $prev eq "") { 472 err("last line in file is blank"); 473} 474 475} 476