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   53205 use strict;
  49         67  
  49         1441  
72 49     49   176 use warnings;
  49         72  
  49         1468  
73 49     49   168 use utf8;
  49         63  
  49         318  
74              
75             package Devel::PerlySense::Plugin::Syntax::Moose;
76             $Devel::PerlySense::Plugin::Syntax::Moose::VERSION = '0.0218';
77              
78              
79              
80              
81              
82 49     49   2891 use Spiffy -Base;
  49         61  
  49         369  
83 49     49   47303 use Carp;
  49     49   85  
  49     49   824  
  49         145  
  49         60  
  49         1073  
  49         154  
  49         65  
  49         2997  
84 49     49   178 use Data::Dumper;
  49         58  
  49         1756  
85 49     49   172 use PPI::Document;
  49         64  
  49         750  
86 49     49   141 use PPI::Dumper;
  49         51  
  49         18821  
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 366541 sub parse {
116 521049         1146128 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     1041888 if ($pkgNode eq "PPI::Token::Word" && $oNode eq "has") {
248 8 50       156 if (ref(my $oNodeStatement = $oNode->parent) eq "PPI::Statement") {
249 8 50       71 if (ref(my $nodeName = $oNode->snext_sibling()) ) {
250 8         251 my $namesSub = "$nodeName";
251              
252             #Special case q and qq
253 8         107 my $refName = ref($nodeName);
254 8 100 66     66 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       13 if (my $nodeListStatement = $nodeName->find_first("PPI::Token::QuoteLike::Words")) {
260 2         759 $namesSub = substr("$nodeListStatement", 2); #Ignore leading "qw"
261             }
262             }
263              
264 8         396 for my $nameSub ( $namesSub =~ /(\w+)/gsm ) {
265             push(
266 11         12 @{$oMeta->raLocationSub},
  11         224  
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         1002086 for my $keyword (qw/ extends with /) {
282             # Slightly fragile, especially wrt comments
283 1042098 100       1787149 if ($pkgNode eq "PPI::Statement") {
284 26344 100       368246 if ($oNode =~ /^ $keyword \s+ (?:qw)? \s* (.+);$/xs) {
285 6         138 my $modules = $1;
286 6         27 for my $module (grep { $_ ne "qw" } $modules =~ /([\w:]+)/gs) {
  10         33  
287 10         28 $rhDataDocument->{rhNameModuleBase}->{$module}++;
288             }
289             }
290             }
291             }
292              
293 521049         1263720 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