File Coverage

lib/Devel/PerlySense/Plugin/Syntax/Moose.pm
Criterion Covered Total %
statement 37 52 71.1
branch 4 16 25.0
condition 2 9 22.2
subroutine 11 11 100.0
pod 1 1 100.0
total 55 89 61.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Devel::PerlySense::Plugin::Syntax::Moose - Plugin for parsing Moose syntax
4             constructs
5              
6             =head1 DESCRIPTION
7              
8             Parses Moose specific syntax, like the "extends" keyword.
9              
10             Currently supported:
11              
12              
13             =over 4
14              
15             =item * has - Attributes
16              
17             Treated as subs (getters/setters).
18              
19             Multiple attributes and overridden attributes are supported.
20              
21             Things like C<handles>, C<clearer>, and C<predicate> aren't supported.
22              
23              
24             =item * extends - Inheritance
25              
26             Single and multiple inheritance supported.
27              
28              
29             =item * with - Roles
30              
31             Treated as base classes.
32              
33              
34             =back
35              
36              
37              
38             =head1 KNOWN MOOSE BUGS
39              
40             Broken Moose code, e.g. multiple extends are parsed incorrectly (the
41             ISA isn't reset). But you shouldn't have broken Moose code should you?
42              
43             Roles are treated like base classes, because that's the most similar
44             Perl concept.
45              
46             Some parts of the parsing is a bit sloppy and fragile, e.g. comments
47             in lists may be picked up.
48              
49              
50              
51             =head1 KNOWN BUGS
52              
53             This plugin module is not yet it's own distribution, which it should
54             be. It should have a base class inside the PerlySense distro to future
55             proof both PerlySense's and the plugins' APIs against each other.
56              
57             The plugins could have some kind of marker for when they should be run
58             for a document. It could be a quick regex on the source or per line or
59             something.
60              
61             Reporting back to PerlySense isn't quite uniform yet in that most
62             things are set in a hash ref, but sub location are set on the Meta
63             object. That should be fixed.
64              
65             =cut
66              
67              
68              
69              
70              
71 45     45   89980 use strict;
  45         70  
  45         2061  
72 45     45   173 use warnings;
  45         59  
  45         1427  
73 45     45   184 use utf8;
  45         111  
  45         402  
74              
75             package Devel::PerlySense::Plugin::Syntax::Moose;
76              
77              
78              
79              
80              
81              
82 45     45   1830 use Spiffy -Base;
  45         69  
  45         352  
83 45     45   48435 use Carp;
  45     45   68  
  45     45   1201  
  45         158  
  45         55  
  45         962  
  45         179  
  45         231  
  45         3211  
84 45     45   187 use Data::Dumper;
  45         59  
  45         1831  
85 45     45   182 use PPI::Document;
  45         59  
  45         1084  
86 45     45   168 use PPI::Dumper;
  45         208  
  45         20247  
87              
88              
89              
90              
91              
92             =head1 PROPERTIES
93              
94             =head1 API METHODS
95              
96             =cut
97              
98              
99              
100              
101              
102             =head2 parse($rhDataDocument, $oMeta, $oDocument, $oNode, $pkgNode, $row, $col, $packageCurrent)
103              
104             Parse the Devel::PerlySense::Document and extract metadata. Fill
105             appropriate data structures.
106              
107             rhDataDocument
108              
109             the key e.g. "Moose" for Plugin::Syntax::Moose, is for the plugin to
110             manage. It's persistent during the complete parse of a document.
111              
112             Return 1 or die on errors.
113              
114             =cut
115 512157     512157 1 387768 sub parse {
116 512157         1992104 my ($rhDataDocument, $oMeta, $oDocument, $oNode, $pkgNode, $row, $col, $packageCurrent) = Devel::PerlySense::Util::aNamedArg(["rhDataDocument", "oMeta", "oDocument", "oNode", "pkgNode", "row", "col", "packageCurrent"], @_);
117              
118             #sub (has getter/setter)
119              
120             ### Bareword
121             # PPI::Statement
122             # PPI::Token::Word 'has'
123             # PPI::Token::Whitespace ' '
124             # PPI::Token::Word 'timeBareword'
125             # PPI::Token::Whitespace ' '
126             # PPI::Token::Operator '=>'
127             # PPI::Token::Whitespace ' '
128             # PPI::Structure::List ( ... )
129             # PPI::Statement::Expression
130             # PPI::Token::Word 'is'
131             # PPI::Token::Whitespace ' '
132             # PPI::Token::Operator '=>'
133             # PPI::Token::Whitespace ' '
134             # PPI::Token::Quote::Double '"rw"'
135             # PPI::Token::Structure ';'
136              
137             ### Quoted
138             # PPI::Statement
139             # PPI::Token::Word 'has'
140             # PPI::Token::Whitespace ' '
141             # PPI::Token::Quote::Double '"timeQuoted"'
142             # PPI::Token::Whitespace ' '
143             # PPI::Token::Operator '=>'
144             # PPI::Token::Whitespace ' '
145             # PPI::Structure::List ( ... )
146             # PPI::Token::Whitespace '\n'
147             # PPI::Token::Whitespace ' '
148             # PPI::Statement::Expression
149             # PPI::Token::Word 'is'
150             # PPI::Token::Whitespace ' '
151             # PPI::Token::Operator '=>'
152             # PPI::Token::Whitespace ' '
153             # PPI::Token::Quote::Double '"rw"'
154             # PPI::Token::Operator ','
155             # PPI::Token::Whitespace '\n'
156             # PPI::Token::Whitespace ' '
157             # PPI::Token::Word 'isa'
158             # PPI::Token::Whitespace ' '
159             # PPI::Token::Operator '=>'
160             # PPI::Token::Whitespace ' '
161             # PPI::Token::Quote::Double '"Int"'
162             # PPI::Token::Operator ','
163             # PPI::Token::Whitespace '\n'
164             # PPI::Token::Structure ';'
165              
166             ### Comma instead of =>
167             # PPI::Statement
168             # PPI::Token::Word 'has'
169             # PPI::Token::Whitespace ' '
170             # PPI::Token::Quote::Double '"timeQuotedComma"'
171             # PPI::Token::Operator ','
172             # PPI::Token::Whitespace ' '
173             # PPI::Structure::List ( ... )
174             # PPI::Statement::Expression
175             # PPI::Token::Word 'is'
176             # PPI::Token::Whitespace ' '
177             # PPI::Token::Operator '=>'
178             # PPI::Token::Whitespace ' '
179             # PPI::Token::Quote::Double '"rw"'
180             # PPI::Token::Structure ';'
181              
182             ### Quoted list
183             # PPI::Statement
184             # PPI::Token::Word 'has'
185             # PPI::Token::Whitespace ' '
186             # PPI::Structure::Constructor [ ... ]
187             # PPI::Statement
188             # PPI::Token::Quote::Double '"timeList1"'
189             # PPI::Token::Operator ','
190             # PPI::Token::Whitespace ' '
191             # PPI::Token::Quote::Double '"timeList2"'
192             # PPI::Token::Whitespace ' '
193             # PPI::Token::Operator '=>'
194             # PPI::Token::Whitespace ' '
195             # PPI::Structure::List ( ... )
196             # PPI::Token::Whitespace '\n'
197             # PPI::Token::Whitespace ' '
198             # PPI::Statement::Expression
199             # PPI::Token::Word 'is'
200             # PPI::Token::Whitespace ' '
201             # PPI::Token::Operator '=>'
202             # PPI::Token::Whitespace ' '
203             # PPI::Token::Quote::Double '"rw"'
204             # PPI::Token::Operator ','
205             # PPI::Token::Whitespace '\n'
206             # PPI::Token::Structure ';'
207              
208             ### Quoted Word list
209             # PPI::Statement
210             # PPI::Token::Word 'has'
211             # PPI::Token::Whitespace ' '
212             # PPI::Structure::Constructor [ ... ]
213             # PPI::Token::Whitespace ' '
214             # PPI::Statement
215             # PPI::Token::QuoteLike::Words 'qw/ timeQwList1 timeQwList2 /'
216             # PPI::Token::Whitespace ' '
217             # PPI::Token::Whitespace ' '
218             # PPI::Token::Operator '=>'
219             # PPI::Token::Whitespace ' '
220             # PPI::Structure::List ( ... )
221             # PPI::Token::Whitespace '\n'
222             # PPI::Token::Whitespace ' '
223             # PPI::Statement::Expression
224             # PPI::Token::Word 'is'
225             # PPI::Token::Whitespace ' '
226             # PPI::Token::Operator '=>'
227             # PPI::Token::Whitespace ' '
228             # PPI::Token::Quote::Double '"ro"'
229             # PPI::Token::Operator ','
230             # PPI::Token::Whitespace '\n'
231             # PPI::Token::Structure ';'
232              
233             ### q/name/
234             # PPI::Statement
235             # PPI::Token::Word 'has'
236             # PPI::Token::Whitespace ' '
237             # PPI::Token::Quote::Literal 'q/timeSingleQuoted/'
238             # PPI::Token::Whitespace ' '
239             # PPI::Token::Operator '=>'
240             # PPI::Token::Whitespace ' '
241             # PPI::Structure::List ( ... )
242             # PPI::Token::Structure ';'
243              
244             ###TODO: Getting the scalar or list contents seems very common. Extract?
245             # What about comments inside a stringified list?
246              
247 512157 50 66     1797059 if ($pkgNode eq "PPI::Token::Word" && $oNode eq "has") {
248 0 0       0 if (ref(my $oNodeStatement = $oNode->parent) eq "PPI::Statement") {
249 0 0       0 if (ref(my $nodeName = $oNode->snext_sibling()) ) {
250 0         0 my $namesSub = "$nodeName";
251              
252             #Special case q and qq
253 0         0 my $refName = ref($nodeName);
254 0 0 0     0 if ($refName eq "PPI::Token::Quote::Literal" || $refName eq "PPI::Token::Quote::Interpolate") {
    0 0        
255 0         0 $namesSub =~ s/\w+//ms; #Remove first word, which should be the quote operator
256             }
257             #Special case qw/ /
258             elsif ($refName eq "PPI::Structure::Constructor" && $nodeName->can("find_first")) {
259 0 0       0 if (my $nodeListStatement = $nodeName->find_first("PPI::Token::QuoteLike::Words")) {
260 0         0 $namesSub = substr("$nodeListStatement", 2); #Ignore leading "qw"
261             }
262             }
263              
264 0         0 for my $nameSub ( $namesSub =~ /(\w+)/gsm ) {
265 0         0 push(
266 0         0 @{$oMeta->raLocationSub},
267             $oMeta->oLocationSub(
268             $oDocument,
269             $oNodeStatement,
270             $nameSub,
271             $packageCurrent,
272             ),
273             );
274             }
275             }
276             }
277             }
278              
279              
280             #base class (ISA and Roles)
281 512157         1252194 for my $keyword (qw/ extends with /) {
282             # Slightly fragile, especially wrt comments
283 1024314 100       2389630 if ($pkgNode eq "PPI::Statement") {
284 25840 50       473622 if ($oNode =~ /^ $keyword \s+ (?:qw)? \s* (.+);$/xs) {
285 0         0 my $modules = $1;
286 0         0 for my $module (grep { $_ ne "qw" } $modules =~ /([\w:]+)/gs) {
  0         0  
287 0         0 $rhDataDocument->{rhNameModuleBase}->{$module}++;
288             }
289             }
290             }
291             }
292              
293 512157         1975065 return(1);
294             }
295              
296              
297              
298              
299              
300             1;
301              
302              
303              
304              
305              
306             __END__
307              
308             =encoding utf8
309              
310             =head1 AUTHOR
311              
312             Johan Lindström, C<< <johanl[ÄT]DarSerMan.com> >>
313              
314             =head1 BUGS
315              
316             Please report any bugs or feature requests to
317             C<bug-devel-perlysense@rt.cpan.org>, or through the web interface at
318             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel-PerlySense>.
319             I will be notified, and then you'll automatically be notified of progress on
320             your bug as I make changes.
321              
322             =head1 ACKNOWLEDGEMENTS
323              
324             =head1 COPYRIGHT & LICENSE
325              
326             Copyright 2005 Johan Lindström, All Rights Reserved.
327              
328             This program is free software; you can redistribute it and/or modify it
329             under the same terms as Perl itself.
330              
331             =cut