Lines Matching +full:multi +full:- +full:line
3 # Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
4 # Copyright Siemens AG 2019-2022
11 # check-format.pl
12 # - check formatting of C source according to OpenSSL coding style
15 # check-format.pl [-l|--sloppy-len] [-l|--sloppy-bodylen]
16 # [-s|--sloppy-space] [-c|--sloppy-comment]
17 # [-m|--sloppy-macro] [-h|--sloppy-hang]
18 # [-e|--eol-comment] [-1|--1-stmt]
21 # run self-tests:
22 # util/check-format.pl util/check-format-test-positives.c
23 # util/check-format.pl util/check-format-test-negatives.c
31 # -l | --sloppy-len increase accepted max line length from 80 to 84
32 # -l | --sloppy-bodylen do not report function body length > 200
33 # -s | --sloppy-space do not report whitespace nits
34 # -c | --sloppy-comment do not report indentation of comments
35 # Otherwise for each multi-line comment the indentation of
40 # refer to the following line and may be indented equally.
41 # -m | --sloppy-macro allow missing extra indentation of macro bodies
42 # -h | --sloppy-hang when checking hanging indentation, do not report
43 # * same indentation as on line before
44 # * same indentation as non-hanging indent level
45 # * indentation moved left (not beyond non-hanging indent)
46 # just to fit contents within the line length limit
47 # -e | --eol-comment report needless intermediate multiple consecutive spaces also before end-of-line comments
48 # -1 | --1-stmt do more aggressive checks for { 1 stmt } - see below
50 # There are non-trivial false positives and negatives such as the following.
52 # * When a line contains several issues of the same kind only one is reported.
54 # * When a line contains more than one statement this is (correctly) reported
66 # Yet with the --1-stmt option false positives are preferred over negatives.
67 # False negatives occur if the braces are more than two non-blank lines apart.
70 # except when this is before end-of-line comments (unless the --eol-comment is given) and
75 # This pattern is recognized - and consequently extra space not reported -
76 # for a given line if in the non-blank line before or after (if existing)
77 # for each occurrence of " \S" (where \S means non-space) in the given line
78 # there is " \S" in the other line in the respective column position.
80 # and false positives (in case of more complex multi-column alignment).
101 # command-line options
111 while ($ARGV[0] =~ m/^-(\w|-[\w\-]+)$/) {
113 if ($arg =~ m/^(l|-sloppy-len)$/) {
115 } elsif ($arg =~ m/^(b|-sloppy-bodylen)$/) {
117 } elsif ($arg =~ m/^(s|-sloppy-space)$/) {
119 } elsif ($arg =~ m/^(c|-sloppy-comment)$/) {
121 } elsif ($arg =~ m/^(m|-sloppy-macro)$/) {
123 } elsif ($arg =~ m/^(h|-sloppy-hang)$/) {
125 } elsif ($arg =~ m/^(e|-eol-comment)$/) {
127 } elsif ($arg =~ m/^(1|-1-stmt)$/) {
130 die("unknown option: -$arg");
135 my $self_test; # whether the current input file is regarded to contain (positive/negative) self-tests
137 my $in_comment; # number of lines so far within multi-line comment, 0 if no comment, < 0 when end is on current line
138 my $leading_comment; # multi-line comment has no code before its beginning delimiter, if $in_comment != 0
139 my $formatted_comment; # multi-line comment beginning with "/*-", which indicates/allows special formatting, if $in_comment != 0
142 my $ifdef__cplusplus; # line before contained '#ifdef __cplusplus' (used in header files)
146 my $preproc_offset; # offset to $block_indent within multi-line preprocessor directive, else 0
147 my $in_macro_header; # number of open parentheses + 1 in (multi-line) header of #define, if $in_preproc != 0
149 my $line; # current line number
150 my $line_before; # number of previous not essentially blank line (containing at most whitespace and '\')
151 my $line_before2; # number of not essentially blank line before previous not essentially blank line
154 my $contents; # contents of current line (without blinding)
155 # $_ # current line, where comments etc. get blinded
156 my $code_contents_before; # contents of previous non-comment non-preprocessor-directive line (without blinding), initially ""
161 my $in_multiline_string; # line starts within multi-line string literal
162 my $count; # -1 or number of leading whitespace characters (except newline) in current line,
164 my $count_before; # number of leading whitespace characters (except line ending chars) in $contents_before
165 my $has_label; # current line contains label
167 my $line_body_start; # number of line where last function body started, or 0
168 my $line_function_start; # number of line where last function definition started, used for $line_body_start
170 my $line_opening_brace; # number of previous line with opening brace after if/do/while/for, optionally for 'else'
177 my $if_maybe_terminated; # 'if' ends and $hanging_offset should be reset unless the next line starts with 'else'
184 my $expr_indent; # resulting hanging indent within (multi-line) expressions including type exprs, else 0
186 my $in_block_decls; # number of local declaration lines after block opening before normal statements, or -1 if no block opening
191 my $num_reports_line = 0; # number of issues found on current line
197 my $num_length_reports = 0;# total number of line length issues found
204 $line = 0;
218 $in_block_decls = -1;
301 my $line = shift;
307 print "$ARGV:$line:$msg:$contents" unless $self_test;
319 report_flexibly($line, $msg, $contents);
322 sub parens_balance { # count balance of opening parentheses - closing parentheses
324 return $str =~ tr/\(// - $str =~ tr/\)//;
327 sub blind_nonspace { # blind non-space text of comment as @, preserving length and spaces
337 sub check_indent { # used for lines outside multi-line string literals
342 m/^\s*\/\*/ ? "intra-line comment" :
350 # allow indent 1 for labels - this cannot happen for leading ':'
354 # leading ':' within stmt/expr/decl - this cannot happen for labels, leading '&&', or leading '||'
356 ($alt_desc, $alt_indent) = ("leading ':'", @nested_conds_indents[-1]);
358 # allow extra indent offset leading '&&' or '||' - this cannot happen for leading ":"
361 if ($expr_indent < 0) { # implies @nested_symbols != 0 && @nested_symbols[0] eq "{" && @nested_indents[-1] < 0
363 # this cannot happen for labels and overrides special treatment of ':', '&&' and '||' for this line
366 @nested_indents[-1] = $count == $stmt_indent ? $stmt_indent : -@nested_indents[-1]; # allow $stmt_indent
367 $ref_indent = $expr_indent = @nested_indents[-1];
370 # check consistency of indentation within multi-line comment (i.e., between its first, inner, and last lines)
371 if ($in_comment != 0 && $in_comment != 1) { # in multi-line comment but not on its first line
374 report("indent = $count != $comment_indent within multi-line comment")
377 my $tweak = $in_comment == -2 ? 1 : 0;
378 report("indent = ".($count + $tweak)." != $comment_indent at end of multi-line comment")
382 # do not check indentation of last line of non-leading multi-line comment
384 s/^(\s*)@/$1*/; # blind first '@' as '*' to prevent below delayed check for the line before
387 return if $in_comment > 0; # not on its last line
388 # $comment_indent will be checked by the below checks for end of multi-line comment
391 # else check indentation of entire-line comment or entire-line end of multi-line comment
392 # ... w.r.t. indent of the following line by delayed check for the line before
393 if (($in_comment == 0 || $in_comment == 1) # no comment, intra-line comment, or begin of multi-line comment
394 && $line_before > 0 # there is a line before
395 && $contents_before_ =~ m/^(\s*)@[\s@]*$/) { # line before begins with '@', no code follows (except '\')
396 report_flexibly($line_before, "entire-line comment indent = $count_before != $count (of following line)",
397 $contents_before) if !$sloppy_cmt && $count_before != -1 && $count_before != $count;
399 # ... but allow normal indentation for the current line, else above check will be done for the line before
400 if (($in_comment == 0 || $in_comment < 0) # (no comment,) intra-line comment or end of multi-line comment
401 && m/^(\s*)@[\s@]*$/) { # line begins with '@', no code follows (except '\')
402 if ($count == $ref_indent) { # indentation is like for (normal) code in this line
403 s/^(\s*)@/$1*/; # blind first '@' as '*' to prevent above delayed check for the line before
406 return if !eof; # defer check of entire-line comment to next line
409 # else check indentation of leading intra-line comment or end of multi-line comment
410 if (m/^(\s*)@/) { # line begins with '@', i.e., any (remaining type of) comment
412 report("intra-line comment indent = $count != $ref_indent") if $in_comment == 0;
413 report("multi-line comment indent = $count != $ref_indent") if $in_comment < 0;
419 # do not report same indentation as on the line before (potentially due to same violations)
426 # apparently aligned to the right in order to fit within line length limit
443 my $terminator_position = -1;
453 $i += length($1) + length($2) - 1;
462 # cancel newly hanging_offset if opening brace '{' is after non-whitespace non-comment:
463 $hanging_offset -= INDENT_LEVEL if $hanging_offset > 0 && $head =~ m/[^\s\@]/;
473 ? $i + 1 + length($1) # actual indentation of following non-space non-comment
475 : -($i + 1); # allow also $stmt_indent if '{' with only whitespace thereafter
485 # # multi-line expr after 'case'
492 @nested_symbols[-1] == $opening_c) { # for $c there was a corresponding $opening_c
514 if $tail =~ m/^([^{]*)/ && $1 =~ m/[^\s\@;]/; # non-space non-';' before any '{'
519 return -1;
542 $self_test = $ARGV =~ m/check-format-test/;
543 $_ = "" if $self_test && m/ blank line within local decls /;
544 $line++;
549 if (m/(.*?)([\x00-\x09\x0B-\x1F\x7F-\xFF])/) {
551 report(($2 eq "\x09" ? "TAB" : $2 eq "\x0D" ? "CR " : $2 =~ m/[\x00-\x1F]/ ? "non-printable"
552 : "non-7bit char") . " at column $col") ;
558 # assign to $count the actual indentation level of the current line
569 # handle multi-line string literals to avoid confusion on starting/ending '"' and trailing '\'
573 $count = -1; # do not check indentation
575 report("multi-line string literal not terminated by '\"' and trailing '\' is missing")
583 s#('[^']*')#$1 =~ tr/'/@/cr#eg; # handle all intra-line character literals
584 s#("[^"]*")#$1 =~ tr/"/@/cr#eg; # handle all intra-line string literals
595 # do/prepare checks within multi-line comments
597 if ($in_comment > 0) { # this still includes the last line of multi-line comment
600 report("missing space or '*' after leading '*' in multi-line comment") if $cmt_text =~ m|^[^*\s/$self_test_exception]|;
602 report("missing leading '*' in multi-line comment");
607 # detect end of comment, must be within multi-line comment, check if it is preceded by non-whitespace text
616 report("text before '*/' in multi-line comment") if ($head =~ m/[^*\s]/); # non-SPC before '*/'
617 $in_comment = -1; # indicate that multi-line comment ends on current line
619 # make indentation of end of multi-line comment appear like of leading intra-line comment
621 $count--;
622 $in_comment = -2; # indicate that multi-line comment ends on current line, with tweak
630 # detect begin of comment, check if it is followed by non-space text
632 if (my ($head, $opt_minus, $tail) = m|^(.*?)/\*(-?)(.*)$|) { # begin of comment: '/*'
638 report("unexpected '/*' inside multi-line comment");
639 } elsif ($tail =~ m|^(.*?)\*/(.*)$|) { # comment end: */ on same line
640 report("unexpected '/*' inside intra-line comment") if $1 =~ /\/\*/;
645 } else { # begin of multi-line comment
647 report("text after '/*' in multi-line comment")
650 # adapt to actual indentation of first line
655 $formatted_comment = $opt_minus eq "-";
657 } elsif (($head, $tail) = m|^\{-(.*)$|) { # begin of Perl pragma: '{-'
660 if ($in_comment > 1) { # still inside multi-line comment (not at its begin or end)
665 # handle special case of line after '#ifdef __cplusplus' (which typically appears in header files)
672 # check for over-long lines,
673 # while allowing trailing (also multi-line) string literals to go past $max_length
674 my $len = length; # total line length (without trailing '\n')
678 # this allows over-long trailing string literals with beginning col before $max_length
680 report("line length = $len > ".MAX_LINE_LENGTH);
683 # handle C++ / C99 - style end-of-line comments
685 report("'//' end-of-line comment"); # the '//' comment style is not allowed for C90
690 # at this point all non-space portions of any types of comments have been blinded as @
696 if (s/^(\s*#)(\s*)(\w+)//) { # line beginning with '#' and directive name;
697 # blank these portions to prevent confusion with C-level 'if', 'else', etc.
703 report("preprocessor directive within multi-line directive");
709 if $preproc_directive =~ m/^(if|elif)$/ && m/^[\W0-9]+$/ && !$trailing_backslash;
710 $preproc_if_nesting-- if $preproc_directive =~ m/^(else|elif|endif)$/;
721 $count = -1; # do not check indentation of first line of preprocessor directive
726 # intra-line whitespace nits @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
728 my $in_multiline_comment = ($in_comment > 1 || $in_comment < 0); # $in_multiline_comment refers to line before
733 $in_comment != 0 ? " in multi-line comment"
734 : " in intra-line comment" : "");
736 sub split_line_head { # split line contents into header containing leading spaces and the first non-space char, and the rest of the line
738 $in_comment != 0 ? "@" : ""; # '@' will match the blinded leading '*' in multi-line comment
739 # $in_comment may pertain to the following line due to delayed check
740 # do not check for extra SPC in leading spaces including any '#' (or '*' within multi-line comment)
747 if ($line_before > 0) { # check with one line delay, such that at least $contents_before is available
748 sub column_alignments_only { # return 1 if the given line has multiple consecutive spaces only at columns that match the reference line
750 my $head = shift; # leading spaces and the first non-space char
751 my $intra = shift; # the rest of the line contents
752 my $contents = shift; # reference line
753 # check if all extra SPC in $intra is used only for multi-line column alignment with $contents
755 for (my $col = 0; $col < length($intra) - 2; $col++) {
757 next unless $substr =~ m/^\s\s\S/; # extra SPC (but not in leading spaces of the line)
758 next if !$eol_cmt && $substr =~ m/^[@\s]+$/; # end-of-line comment
759 return 0 unless substr($contents, $col + $offset + 1, 2) =~ m/\s\S/; # reference line contents do not match
764 !( column_alignments_only($head1, $intra_line1, $_ ) # compare with $line
769 } elsif (eof) { # special case: just one line exists
776 || ($intra_line =~ m/(\S)([\+\-\*\/\/%\&\|\^\!<>=]=)/
779 && !($1 =~ m/[\+\-\*\/\/%\&\|\^\!<>=]/)
782 $intra_line =~ s/(<<|>>|[\+\-\*\/\/%\&\|\^\!<>=])=/=/g;
784 $intra_line =~ s/[A-Z_]+/int/g if $trailing_backslash;
802 report("space before '$1'") if $intra_line =~ m/[\w)\]]\s+(\+\+|--)/; # postfix ++/-- with preceding space
803 report("space after '$1'") if $intra_line =~ m/(\+\+|--)\s+[a-zA-Z_(]/; # prefix ++/-- with following space
805 report("space before '$1'") if $intra_line =~ m/\s(\.|->)/; # '.' or '->' with preceding space
806 report("space after '$1'") if $intra_line =~ m/(\.|->)\s/; # '.' or '->' with following space
807 $intra_line =~ s/\-\>|\+\+|\-\-/@/g; # blind '->,', '++', and '--'
815 report("missing space before binary '$2'") if $intra_line =~ m/([^\s{()\[e])([+\-])/; # '+'/'-' without preceding space or {()[e
816 # ')' may be used for type casts or before "->", 'e' may be used for numerical literals such as "1e-6"
821 report("missing space after binary '$1'") if $intra_line=~m/[^{(\[]([*])[^\sa-zA-Z_(),*]/;# '*' w/o space or \w(),* after
823 report("missing space after binary '$1'") if $intra_line=~m/([&])[^\sa-zA-Z_(]/; # '&' w/o following space or \w(
825 report("missing space after binary '$1'") if $intra_line=~m/[^{(\[]([+\-])[^\s\d(]/; # +/- w/o following space or \d(
826 # TODO unary '+' and '-' must not be followed by SPC
839 s/(\w*ASN1_[A-Z_]+END\w*([^(]|\(.*?\)|$))/$1;/g; # treat *ASN1_*END*(..) macro calls as if followed by ';'
847 $in_block_decls = -1;
852 $hanging_symbol = @nested_symbols[-1];
853 $expr_indent = @nested_indents[-1];
858 $local_offset = -INDENT_LEVEL;
883 # with non-whitespace non-'{' before
887 my $body_len = $line - $line_body_start - 1;
892 if ($before ne "") { # non-whitespace non-'{' before '}'
895 $in_block_decls = -1;
896 $local_offset = $block_indent + $hanging_offset - INDENT_LEVEL;
899 $local_offset -= ($block_indent + $hanging_offset);
900 # in effect $local_offset = -INDENT_LEVEL relative to $block_indent + $hanging_offset values before
904 # handle opening brace '{' after if/else/while/for/switch/do on line before
909 $hanging_offset -= INDENT_LEVEL; # cancel newly hanging_offset
915 $local_offset = -INDENT_LEVEL;
918 $local_offset = -INDENT_LEVEL;
924 # potential adaptations of indent in first line of macro body in multi-line macro definition
930 if ($count == $block_indent - $preproc_offset # body began with same indentation as preceding code
932 $block_indent -= $preproc_offset;
940 check_indent() if $count >= 0; # not for start of preprocessor directive and not if multi-line string literal is continued
945 $in_comment == 0 && !m/^\s*\*?@/ && # not in a multi-line or intra-line comment
947 my $blank_line_before = $line > 1 && $code_contents_before =~ m/^\s*(\\\s*)?$/;
948 # essentially blank line before: just whitespace and maybe a '\'
950 || (m/^(\s*(\w+|\[\]|[\*()]))+?\s+[\*\(]*\w+(\s*(\)|\[[^\]]*\]))*\s*[;,=]/ # weak check for decl involving user-defined type
953 report_flexibly($line - 1, "blank line within local decls, before", $contents) if $blank_line_before;
955 report_flexibly($line, "missing blank line after local decls", "\n$contents_before$contents")
957 $in_block_decls = -1 unless
958 m/^\s*(\\\s*)?$/ # essentially blank line: just whitespace (and maybe a trailing '\')
959 || $in_comment != 0 || m/^\s*\*?@/; # in multi-line comment or an intra-line comment
963 $in_comment = 0 if $in_comment < 0; # multi-line comment has ended
967 my $outermost_level = $block_indent - $preproc_offset == 0;
969 report("more than one stmt") if !m/(^|\W)for(\W.*|$)/ && # no 'for' - TODO improve matching
972 # check for code block containing a single line/statement
976 # TODO extend detection from single-line to potentially multi-line statement
985 # follows with a block containing more than one line/statement
990 report("single-letter name '$2'") if (m/(^|.*\W)([IO])(\W.*|$)/); # single-letter name 'I' or 'O' # maybe re-add 'l'?
993 if (m/(['"]|([\+\-\*\/\/%\&\|\^<>]\s*)?\W[0-9]+L?|\WNULL)\s*([\!<>=]=|[<=>])([<>]?)/ &&
1006 $tmp =~ s/[\!<>=]=/@@/g; # blind (in-)equality symbols like '<=' as '@@' to prevent matching them as '=' below
1020 # to cope with multi-line expressions, do this also if !($tail =~ m/\{/)
1038 my $code_before = $head =~ m/[^\s\@}]/; # leading non-whitespace non-comment non-'}'
1040 report("code after '$mid'" ) if $tail =~ m/[^\s\@{]/# trailing non-whitespace non-comment non-'{' (non-'\')
1053 $hanging_offset = length($head) - $block_indent;
1069 $hanging_offset += INDENT_LEVEL if m/\*.*\(/; # '*' followed by '(' - seems consistent with Emacs C mode
1076 # on end of non-if/while/for/switch (multi-line) expression (i.e., return/enum/assignment) and
1092 $hanging_offset -= INDENT_LEVEL;
1102 $in_typedecl-- if $in_typedecl != 0 && @nested_in_typedecl == 0; # TODO handle multiple type decls per line
1104 $terminator_position = length($_) - length($1) if $1;
1105 # new $terminator_position value may be after the earlier one in case multiple terminators on current line
1106 # TODO check treatment in case of multiple terminators on current line
1110 # set hanging expression indent according to nested indents - TODO maybe do better in update_nested_indents()
1113 for (my $i = -1; $i >= -@nested_symbols; $i--) {
1122 # remember line number and header containing name of last function defined for reports w.r.t. MAX_BODY_LENGTH
1124 $line_function_start = $line;
1128 # special checks for last, typically trailing opening brace '{' in line
1134 report("'{' not at line start") if length($head) != $preproc_offset && $head =~ m/\)\s*/; # at end of function definition header
1135 $line_body_start = $contents =~ m/LONG BODY/ ? 0 : $line if $line_function_start != 0;
1138 $line_opening_brace = $line if $keyword_opening_brace =~ m/if|do|while|for/;
1139 # using, not assigning, $keyword_opening_brace here because it could be on an earlier line
1140 $line_opening_brace = $line if $keyword_opening_brace eq "else" && $extended_1_stmt &&
1141 # TODO prevent false positives for if/else where braces around single-statement branches
1146 report("code after '{'") if $tail=~ m/[^\s\@]/ && # trailing non-whitespace non-comment (non-'\')
1151 # check for opening brace after if/while/for/switch/do not on same line
1152 # note that "missing '{' on same line after '} else'" is handled further below
1157 report("'{' not on same line as preceding '$mid'") if !$brace_after;
1159 # check for closing brace on line before 'else' not followed by leading '{'
1161 if (parens_balance($tail) == 0 && # avoid false positive due to unfinished expr on current line
1162 !($tail =~ m/{/) && # after 'else' missing '{' on same line
1164 $line_before > 0 && $contents_before_ =~ /}[\s@]*$/) { # trailing '}' on line before
1165 report("missing '{' on same line after '} else'");
1169 # check for closing brace before 'while' not on same line
1175 parens_balance($tail) == 0 && # avoid false positive due to unfinished expr on current line
1178 $contents_before_ =~ /}[\s@]*$/) { # on line before: '}' then any whitespace or comments
1179 report("'while' not on same line as preceding '}'");
1183 # check for missing brace on same line before or after 'else'
1190 report("'else' not on same line as preceding '}'");
1191 } elsif (parens_balance($tail) == 0) { # avoid false positive due to unfinished expr on current line
1192 report("missing '}' on same line before 'else ... {'") if $brace_after;
1194 } elsif (parens_balance($tail) == 0) { # avoid false positive due to unfinished expr on current line
1195 report("missing '{' on same line after '} else'") if $brace_before && !$brace_after;
1199 # on begin of multi-line preprocessor directive, adapt indent
1202 if ($in_preproc == 1) { # start of multi-line preprocessor directive
1211 # post-processing at end of line @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1215 !m/^\s*#(\s*)(\w+)/ && # not single-line preprocessor directive
1216 $in_comment == 0 && !m/^\s*\*?@/; # not in a multi-line comment nor in an intra-line comment
1218 # on end of (possibly multi-line) preprocessor directive, adapt indent
1226 report("leading ".($1 eq "" ? "blank" :"whitespace")." line") if $line == 1 && !$sloppy_SPC;
1229 my $linediff = $line - $line_before - 1;
1235 $line_before = $line;
1244 print("$ARGV:$line:$num_reports_line reports on:$contents")
1249 # post-processing at end of file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1252 # check for essentially blank line (which may include a '\') just before EOF
1253 report(($1 eq "\n" ? "blank line" : $2 ne "" ? "'\\'" : "whitespace")." at EOF")
1256 # report unclosed expression-level nesting
1259 # sanity-check balance of block-level { ... } via final $block_indent at end of file
1260 report_flexibly($line, +@nested_block_indents." unclosed '{'", "(EOF)\n") if @nested_block_indents != 0;
1262 # sanity-check balance of #if ... #endif via final preprocessor directive indent at end of file
1263 report_flexibly($line, "$preproc_if_nesting unclosed '#if'", "(EOF)\n") if $preproc_if_nesting != 0;
1271 my $num_other_reports = $num_reports - $num_indent_reports - $num_nesting_issues
1272 - $num_syntax_issues - $num_SPC_reports - $num_length_reports;