File Coverage

lib/Devel/PerlySense/Plugin/Syntax/Moose.pm
Criterion Covered Total %
statement 52 52 100.0
branch 14 16 87.5
condition 7 9 77.7
subroutine 11 11 100.0
pod 1 1 100.0
total 85 89 95.5


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 49     49   51799 use strict;
  49         73  
  49         1439  
72 49     49   199 use warnings;
  49         67  
  49         1434  
73 49     49   175 use utf8;
  49         52  
  49         320  
74              
75             package Devel::PerlySense::Plugin::Syntax::Moose;
76             $Devel::PerlySense::Plugin::Syntax::Moose::VERSION = '0.0217';
77              
78              
79              
80              
81              
82 49     49   2847 use Spiffy -Base;
  49         64  
  49         356  
83 49     49   46550 use Carp;
  49     49   61  
  49     49   818  
  49         142  
  49         58  
  49         1065  
  49         160  
  49         54  
  49         3025  
84 49     49   192 use Data::Dumper;
  49         72  
  49         1744  
85 49     49   177 use PPI::Document;
  49         57  
  49         763  
86 49     49   142 use PPI::Dumper;
  49         51  
  49         18916  
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 521049     521049 1 363461 sub parse {
116 521049         1157400 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 521049 100 100     1043280 if ($pkgNode eq "PPI::Token::Word" && $oNode eq "has") {
248 8 50       101 if (ref(my $oNodeStatement = $oNode->parent) eq "PPI::Statement") {
249 8 50       50 if (ref(my $nodeName = $oNode->snext_sibling()) ) {
250 8         153 my $namesSub = "$nodeName";
251              
252             #Special case q and qq
253 8         74 my $refName = ref($nodeName);
254 8 100 66     53 if ($refName eq "PPI::Token::Quote::Literal" || $refName eq "PPI::Token::Quote::Interpolate") {
    100 66        
255 1         5 $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 3 100       6 if (my $nodeListStatement = $nodeName->find_first("PPI::Token::QuoteLike::Words")) {
260 2         370 $namesSub = substr("$nodeListStatement", 2); #Ignore leading "qw"
261             }
262             }
263              
264 8         302 for my $nameSub ( $namesSub =~ /(\w+)/gsm ) {
265             push(
266 11         9 @{$oMeta->raLocationSub},
  11         181  
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 521049         992915 for my $keyword (qw/ extends with /) {
282             # Slightly fragile, especially wrt comments
283 1042098 100       1775324 if ($pkgNode eq "PPI::Statement") {
284 26344 100       362241 if ($oNode =~ /^ $keyword \s+ (?:qw)? \s* (.+);$/xs) {
285 6         135 my $modules = $1;
286 6         24 for my $module (grep { $_ ne "qw" } $modules =~ /([\w:]+)/gs) {
  10         16  
287 10         25 $rhDataDocument->{rhNameModuleBase}->{$module}++;
288             }
289             }
290             }
291             }
292              
293 521049         1262035 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 Lindstrom, C<< <johanl@cpan.org> >>
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 Lindstrom, 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