2 # Copyright (C) 2006-2009, Parrot Foundation.
8 use lib qw( . lib ../lib ../../lib );
9 use Test::More tests => 2;
10 use Parrot::Distribution;
14 t/codingstd/c_indent.t - checks for rules related to indenting in C source
19 % prove t/codingstd/c_indent.t
22 % perl t/codingstd/c_indent.t src/foo.c include/parrot/bar.h
26 Checks that all C language source files have the proper use of indentation,
27 as specified in PDD07.
31 L<docs/pdds/pdd07_codingstd.pod>
38 : map { $_->path() } Parrot::Distribution->new()->get_c_language_files();
43 my ( @pp_indent, @c_indent );
44 my ( %pp_failed, %c_failed );
46 foreach my $path (@_) {
48 open my $IN, '<', $path
49 or die "Can not open '$path' for reading!\n";
61 foreach my $line (@source) {
66 $state{prev_last_char} = $state{last_char};
67 $state{last_char} = substr( $line, -1, 1 );
69 # ignore multi-line comments (except the first line)
70 $state{in_comment} = 0, next if $state{in_comment} &&
73 next if $state{in_comment};
74 $state{in_comment} = 1
79 if ( $line =~ m/^\s*\#(\s*)(ifndef|ifdef|if)\s+(.*)/ )
81 my ($prespace, $condition, $postspace) = ($1,$2,$3);
82 next if ($line =~ m/PARROT_IN_CORE|_GUARD/);
83 next if ($line =~ m/__cplusplus/);
85 my $indent = q{ } x @{ $state{stack} };
86 if ( $prespace ne $indent ) {
87 push @pp_indent => "$path:$state{line_cnt}\n"
89 . "expected: #$indent$condition $postspace'\n";
90 $pp_failed{"$path\n"} = 1;
92 push @{ $state{stack} }, "#$condition $postspace";
96 if ( $line =~ m/^\s*\#(\s*)(else|elif)/)
99 my ($prespace, $condition) = ($1,$2);
100 # stay where we are, but indenting should be
101 # back even with the opening brace.
102 my $indent = q{ } x ( @{ $state{stack} } - 1 );
103 if ( $prespace ne $indent ) {
104 push @pp_indent => "$path:$state{line_cnt}\n"
106 . "expected: #$indent$condition -- it's inside of "
107 . ( join ' > ', @{ $state{stack} } ) . "\n";
108 $pp_failed{"$path\n"} = 1;
112 if ( $line =~ m/^\s*\#(\s*)(endif)/)
114 my ($prespace, $condition) = ($1,$2);
115 my $indent = q{ } x ( @{ $state{stack} } - 1 );
116 if ( $prespace ne $indent ) {
117 push @pp_indent => "$path:$state{line_cnt}\n"
119 . "expected: #$indent$condition -- it's inside of "
120 . ( join ' > ', @{ $state{stack} } ) . "\n";
121 $pp_failed{"$path\n"} = 1;
123 pop @{ $state{stack} };
126 next unless @{ $state{stack} };
128 if ( $line =~ m/^\s*\#(\s*)(.*)/)
130 my ($prespace, $condition) = ($1,$2);
131 next if ($line =~ m/ASSERT_ARGS_/); # autogenerated by headerizer
132 my $indent = q{ } x (@{ $state{stack} });
133 if ( $prespace ne $indent ) {
134 push @pp_indent => "$path:$state{line_cnt}\n"
136 . "expected: #$indent$condition -- it's inside of "
137 . ( join ' > ', @{ $state{stack} } ) . "\n";
138 $pp_failed{"$path\n"} = 1;
144 # for now just try to catch glaring errors. A real parser is
145 # probably overkill for this task. For now we just check the
146 # first line of a function, and assume that more likely than not
147 # indenting is consistent within a func body.
148 if ($line =~ /^(\s*).*\{\s*$/) {
151 # note the beginning of a block, and its indent depth.
152 $state{bif} = length($prespace);
156 if ($line =~ /^\s*([\#\}])/) {
158 my $closing_punc = $1;
159 # skip the last line of the func or cpp directives.
160 $state{bif} = undef if ( $closing_punc eq "}" );
164 if ( defined($state{bif}) ) {
166 # first line of a block
167 if ( $state{bif} == 0 ) {
169 # first line of a top-level block (first line of a function,
171 my ($indent) = $line =~ /^(\s*)/;
172 if ( length($indent) != 4 ) {
173 push @c_indent => "$path:$state{line_cnt}\n"
174 . " apparent non-4 space indenting ("
177 $c_failed{"$path\n"} = 1;
183 my ($indent) = $line =~ /^(\s+)/ or next;
184 $indent = length($indent);
186 # Ignore the indentation of the current line if the last
187 # character of the was anything but a ';'.
189 # The indentation of the previous line is not considered.
190 # Check sanity by verifying that the indentation of the current line
191 # is divisible by four, unless it should be outdented by 2.
192 if ($line =~ m{: (?:\s* /\* .*? \*/)? $}x) {
193 if ( $indent % 4 != 2 &&
194 !$state{in_comment} &&
195 $state{prev_last_char} eq ';'
197 push @c_indent => "$path:$state{line_cnt}\n"
198 . " apparent non-2 space outdenting ($indent spaces)\n";
199 $c_failed{"$path\n"} = 1
204 !$state{in_comment} &&
205 $state{prev_last_char} eq ';'
207 push @c_indent => "$path:$state{line_cnt}\n"
208 . " apparent non-4 space indenting ($indent space"
209 . ( $indent == 1 ? '' : 's' ) . ")\n";
210 $c_failed{"$path\n"} = 1;
216 # get the lists of files failing the test
217 my @c_failed_files = keys %c_failed;
218 my @pp_failed_files = keys %pp_failed;
220 ## L<PDD07/Code Formatting/"Preprocessor #directives must be indented two columns per nesting level, with two exceptions: neither PARROT_IN_CORE nor the outermost _GUARD #ifdefs cause the level of indenting to increase">
221 ok( !scalar(@pp_indent), 'Correctly indented preprocessor directives' )
222 or diag( "incorrect indenting in preprocessor directive found "
225 . scalar @pp_failed_files
226 . " files:\n@pp_indent" );
228 ok( !scalar(@c_indent), 'Correctly indented C files' )
229 or diag( "incorrect indenting in C file found "
232 . scalar @c_failed_files
233 . " files:\n@c_indent" );
236 # dump_state() may be used to diagnose indentation problems.
237 # dump_state(\%state, $line);
238 # Takes a list of two arguments: reference to %state and the current line
239 # (once it has been chomped).
240 # Prints pipe-delimited list of important features of current state.
242 my ($state, $line) = @_;
243 print STDERR (join q{|} => (
245 (defined($state->{bif}) ? $state->{bif} : q{u}),
246 $state->{in_comment},
247 (join q{*} => @{ $state->{stack} }),
254 # cperl-indent-level: 4
257 # vim: expandtab shiftwidth=4: