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