File Coverage

blib/lib/Module/Checkstyle/Check/Block.pm
Criterion Covered Total %
statement 58 90 64.4
branch 14 52 26.9
condition 1 18 5.5
subroutine 10 11 90.9
pod 3 3 100.0
total 86 174 49.4


line stmt bran cond sub pod time code
1             package Module::Checkstyle::Check::Block;
2              
3 2     2   3982 use strict;
  2         4  
  2         85  
4 2     2   12 use warnings;
  2         4  
  2         73  
5              
6 2     2   65 use Carp qw(croak);
  2         4  
  2         487  
7 2     2   974 use Readonly;
  2         8855  
  2         122  
8              
9 2     2   593 use Module::Checkstyle::Util qw(:problem :args);
  2         5  
  2         588  
10              
11 2     2   13 use base qw(Module::Checkstyle::Check);
  2         3  
  2         4981  
12              
13             # The directives we provide
14             Readonly my $DEFAULT_STYLE => 'default-style';
15             Readonly my $OPENING_CURLY => 'opening-curly';
16             Readonly my $CLOSING_CURLY => 'closing-curly';
17             Readonly my $IGNORE_SAME => 'ignore-on-same-line';
18              
19             # Default styles for well known bracketing
20             Readonly my %STYLE => (
21             'bsd' => {
22             $OPENING_CURLY => 'alone',
23             $CLOSING_CURLY => 'alone',
24             $IGNORE_SAME => 1,
25             },
26             'gnu' => {
27             $OPENING_CURLY => 'alone',
28             $CLOSING_CURLY => 'alone',
29             $IGNORE_SAME => 1,
30             },
31             'k&r' => {
32             $OPENING_CURLY => 'same',
33             $CLOSING_CURLY => 'same',
34             $IGNORE_SAME => 1,
35             },
36             'wts' => {
37             $OPENING_CURLY => 'alone',
38             $CLOSING_CURLY => 'alone',
39             $IGNORE_SAME => 1,
40             },
41             'pbp' => {
42             $OPENING_CURLY => 'same',
43             $CLOSING_CURLY => 'alone',
44             $IGNORE_SAME => 1,
45             }
46             );
47              
48             sub register {
49             return (
50 0     0 1 0 'PPI::Structure::Block' => \&handle_block,
51             );
52             }
53              
54             sub new {
55 1     1 1 4 my ($class, $config) = @_;
56            
57 1         13 my $self = $class->SUPER::new($config);
58              
59             # Set defaults if such is configured
60 1 50       6 if ($config->get_directive($DEFAULT_STYLE)) {
61 0         0 my $default_style = lc($config->get_directive($DEFAULT_STYLE));
62 0 0       0 if (exists $STYLE{$default_style}) {
63 0         0 for my $directive (keys %{$STYLE{$default_style}}) {
  0         0  
64 0         0 $self->{$directive} = $STYLE{$default_style}->{$directive};
65             }
66             }
67             }
68            
69             # Opening curly
70 1         5 my $opening_curly = $config->get_directive($OPENING_CURLY);
71 1 50       4 if ($opening_curly) {
72 1 50       8 croak qq/Invalid setting '$opening_curly' for directive '$OPENING_CURLY' in [Block]/ if !is_valid_position($opening_curly);
73 1         19 $self->{$OPENING_CURLY} = lc($opening_curly);
74             }
75              
76             # Closing curly
77 1         12 my $closing_curly = $config->get_directive($CLOSING_CURLY);
78 1 50       5 if ($closing_curly) {
79 0 0       0 croak qq/Invalid setting '$closing_curly' for directive '$CLOSING_CURLY' in [Block]/ if !is_valid_position($closing_curly);
80 0         0 $self->{$CLOSING_CURLY} = lc($closing_curly);
81             }
82              
83             # Ignore same line
84 1         6 my $ignore_same_line = $config->get_directive($IGNORE_SAME);
85 1 50       7 if ($ignore_same_line) {
86 0         0 $self->{$IGNORE_SAME} = as_true($ignore_same_line);
87             }
88            
89 1         4 return $self;
90             }
91              
92             Readonly my %HANDLE_PARENT_STATEMENT => (
93             'PPI::Statement' => 1,
94             'PPI::Statement::Compound' => 1,
95             'PPI::Statement::Scheduled' => 1,
96             'PPI::Statement::Sub' => 1,
97             'PPI::Statement::Variable' => 1,
98             );
99              
100             sub handle_block {
101 5     5 1 16093 my ($self, $block, $file) = @_;
102              
103             # This exists to support C, C etc.
104             # since it's very very common
105 5 50 33     20 if (exists $self->{$IGNORE_SAME} && $self->{$IGNORE_SAME}) {
106 0         0 my $opening_curly = $block->first_token(); # That is the '{'
107 0         0 my $closing_curly = $block->last_token(); # That is the '}'
108 0 0       0 return () if $opening_curly->location()->[0] == $closing_curly->location()->[0];
109             }
110              
111 5         32 my @problems;
112              
113             # Check opening and closing curlies
114 5         13 push @problems, $self->_handle_opening_curly($block, $file);
115 4         10 push @problems, $self->_handle_closing_curly($block, $file);
116              
117 4         17 return @problems;
118             }
119              
120             sub _handle_opening_curly {
121 5     5   6 my ($self, $block, $file) = @_;
122              
123 5         4 my @problems;
124              
125 5         11 my $mode = $self->{$OPENING_CURLY};
126 5 50       27 if ($mode) {
127             # Check parent to see if we should handle this block
128 5         93 my $statement = $block->statement();
129 4 50       71 return () if !$statement;
130 4 50       20 return () if !exists $HANDLE_PARENT_STATEMENT{ref $statement};
131              
132 4         41 my $opening_curly = $block->first_token(); # That is the '{'
133 4         63 my $previous_sibling = $block->sprevious_sibling();
134              
135 4 50       105 if ($previous_sibling) {
136 4         21 my $owner = $previous_sibling->last_token();
137 4         57 my $curly_on_line = $opening_curly->location()->[0];
138 4         46 my $owner_on_line = $owner->location()->[0];
139            
140 4 50       38 if ($mode eq 'same') {
    0          
141 4 100       12 if ($curly_on_line != $owner_on_line) {
142 1         12 push @problems, new_problem($self->config, $OPENING_CURLY,
143             qq/Opening curly is on its own line/,
144             $opening_curly,
145             $file);
146             }
147             }
148             elsif ($mode eq 'alone') {
149 0 0       0 if ($curly_on_line - 1 != $owner_on_line) {
150 0         0 push @problems, new_problem($self->config, $OPENING_CURLY,
151             qq/Opening curly is not the first new line/,
152             $opening_curly,
153             $file);
154             }
155             }
156             }
157             }
158            
159 4         7 return @problems;
160             }
161              
162             sub _handle_closing_curly {
163 4     4   5 my ($self, $block, $file) = @_;
164              
165 4         4 my @problems;
166              
167 4         10 my $mode = $self->{$CLOSING_CURLY};
168 4 50       20 if ($mode) {
169             # Check parent to see if we should handle this block
170 0         0 my $statement = $block->statement();
171 0 0       0 return () if !$statement;
172 0 0       0 return () if !exists $HANDLE_PARENT_STATEMENT{ref $statement};
173              
174 0         0 my $closing_curly = $block->last_token(); # That is the '}'
175 0         0 my $closing_curly_line = $closing_curly->location()->[0];
176 0         0 my $next_statement = $block->snext_sibling();
177 0         0 my @block_contents = $block->schildren();
178 0         0 my $last_block_statement = pop @block_contents;
179 0 0       0 my $previous_statement = defined $last_block_statement ? $last_block_statement->last_token() : undef;
180            
181 0 0       0 if ($mode eq 'same') {
    0          
182 0 0 0     0 if (ref $next_statement && $next_statement->isa('PPI::Token::Word') &&
      0        
183             $closing_curly_line != $next_statement->location()->[0]) {
184 0         0 my $word = $next_statement->content();
185 0         0 push @problems, new_problem($self->config(), $CLOSING_CURLY,
186             qq/Closing curly is not on the same line as following '$word'/,
187             $closing_curly,
188             $file);
189             }
190             }
191             elsif ($mode eq 'alone') {
192 0 0 0     0 if (ref $next_statement && $next_statement->isa('PPI::Token::Word') &&
      0        
193             $closing_curly_line == $next_statement->location()->[0]) {
194 0         0 my $word = $next_statement->content();
195 0         0 push @problems, new_problem($self->config(), $CLOSING_CURLY,
196             qq/Closing curly is on the same line as following '$word'/,
197             $closing_curly,
198             $file);
199             }
200            
201 0 0 0     0 if (ref $previous_statement &&
202             $closing_curly_line == $previous_statement->location()->[0]) {
203 0         0 push @problems, new_problem($self->config(), $CLOSING_CURLY,
204             qq/Closing curly is on the same line as the preceding statement/,
205             $closing_curly,
206             $file);
207             }
208             }
209             }
210            
211 4         6 return @problems;
212             }
213              
214             1;
215             __END__