archruby 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/README.md +1 -1
  4. data/Rakefile +46 -0
  5. data/TODO.rtf +20 -0
  6. data/architecture.yml +51 -0
  7. data/archruby.gemspec +3 -0
  8. data/bin/archruby +1 -0
  9. data/lib/archruby.rb +14 -2
  10. data/lib/archruby/architecture/architecture.rb +6 -6
  11. data/lib/archruby/architecture/config_definition.rb +13 -13
  12. data/lib/archruby/architecture/constraint_break.rb +1 -1
  13. data/lib/archruby/architecture/dependency.rb +3 -1
  14. data/lib/archruby/architecture/file_content.rb +2 -2
  15. data/lib/archruby/architecture/module_definition.rb +33 -20
  16. data/lib/archruby/architecture/parser.rb +25 -11
  17. data/lib/archruby/architecture/type_inference_checker.rb +29 -13
  18. data/lib/archruby/presenters/dsm.rb +163 -0
  19. data/lib/archruby/presenters/dsm/cell_dsm.rb +17 -0
  20. data/lib/archruby/presenters/dsm/dsm_css.css +77 -0
  21. data/lib/archruby/presenters/graph.rb +12 -9
  22. data/lib/archruby/ruby/parser.rb +131 -125
  23. data/lib/archruby/ruby/type_inference/dependency_organizer.rb +53 -0
  24. data/lib/archruby/ruby/type_inference/ruby/class_dependency.rb +22 -0
  25. data/lib/archruby/ruby/type_inference/ruby/internal_method_invocation.rb +20 -0
  26. data/lib/archruby/ruby/type_inference/ruby/method_definition.rb +20 -0
  27. data/lib/archruby/ruby/type_inference/ruby/parser_for_typeinference.rb +537 -0
  28. data/lib/archruby/ruby/type_inference/ruby/process_method_arguments.rb +155 -0
  29. data/lib/archruby/ruby/type_inference/ruby/process_method_body.rb +427 -0
  30. data/lib/archruby/ruby/type_inference/ruby/process_method_params.rb +276 -0
  31. data/lib/archruby/ruby/type_inference/type_inference_checker.rb +126 -0
  32. data/lib/archruby/version.rb +1 -1
  33. data/spec/architecture/file_content_spec.rb +2 -1
  34. data/spec/architecture/module_definition_spec.rb +9 -9
  35. data/spec/dummy_app/lib/teste_class.rb +6 -0
  36. data/spec/ruby/type_inference/dependency_organizer_spec.rb +20 -0
  37. data/spec/ruby/type_inference/fixtures/homebrew_bottles_class.rb +139 -0
  38. data/spec/ruby/type_inference/fixtures/homebrew_brew_teste.rb +1323 -0
  39. data/spec/ruby/type_inference/fixtures/rails_action_view_class_teste.rb +89 -0
  40. data/spec/ruby/type_inference/fixtures/rails_active_record_class.rb +99 -0
  41. data/spec/ruby/type_inference/fixtures/rails_teste_active_record.rb +55 -0
  42. data/spec/ruby/type_inference/fixtures/teste2.rb +16 -0
  43. data/spec/ruby/type_inference/fixtures/teste_class_and_args.rb +49 -0
  44. data/spec/ruby/type_inference/fixtures/teste_class_and_args2.rb +11 -0
  45. data/spec/ruby/type_inference/parser_for_typeinference_spec.rb +69 -0
  46. data/spec/ruby/type_inference/type_inference_checker_spec.rb +47 -0
  47. metadata +84 -3
@@ -0,0 +1,53 @@
1
+ module Archruby
2
+ module Ruby
3
+ module TypeInference
4
+
5
+ class DependencyOrganizer
6
+ attr_reader :dependencies, :method_definitions
7
+
8
+ def initialize
9
+ @dependencies = {}
10
+ @method_definitions = {}
11
+ end
12
+
13
+ def add_dependencies(found_dependencies)
14
+ found_dependencies.each do |class_dependency|
15
+ class_name = class_dependency.name
16
+ @dependencies[class_name] ||= Set.new
17
+ @dependencies[class_name].merge(class_dependency.dependencies)
18
+ end
19
+ end
20
+
21
+ def add_method_calls(found_calls)
22
+ found_calls.each do |method_definition|
23
+ next if unused_method_definition?(method_definition)
24
+ method_name = method_definition.method_name
25
+ class_name = method_definition.class_name
26
+ args = method_definition.args
27
+ internal_method_calls = []
28
+ method_definition.method_calls.each do |internal_method_call|
29
+ next if unused_internal_method_call?(internal_method_call)
30
+ internal_method_calls << internal_method_call
31
+ end
32
+ if !internal_method_calls.empty?
33
+ method_def = Ruby::MethodDefinition.new(class_name, method_name, args, internal_method_calls)
34
+ @method_definitions[class_name] ||= []
35
+ @method_definitions[class_name] << method_def
36
+ end
37
+ end
38
+ end
39
+
40
+ def unused_method_definition?(method_definition)
41
+ method_definition.method_calls.empty? || method_definition.class_name.to_s.empty?
42
+ end
43
+
44
+ def unused_internal_method_call?(internal_method_call)
45
+ internal_method_call.params.nil? ||
46
+ internal_method_call.params.empty? ||
47
+ internal_method_call.class_name.to_s.empty?
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ module Archruby
2
+ module Ruby
3
+ module TypeInference
4
+ module Ruby
5
+
6
+ class ClassDependency
7
+ attr_reader :dependencies, :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ @dependencies = Set.new
12
+ end
13
+
14
+ def add_dependency(class_name)
15
+ @dependencies.add(class_name)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Archruby
2
+ module Ruby
3
+ module TypeInference
4
+ module Ruby
5
+
6
+ class InternalMethodInvocation
7
+ attr_reader :class_name, :method_name, :params, :linenum
8
+
9
+ def initialize(class_name, method_name, params=nil, linenum = nil)
10
+ @class_name = class_name
11
+ @method_name = method_name
12
+ @params = params
13
+ @linenum = linenum
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Archruby
2
+ module Ruby
3
+ module TypeInference
4
+ module Ruby
5
+
6
+ class MethodDefinition
7
+ attr_reader :class_name, :method_name, :args, :method_calls
8
+
9
+ def initialize(class_name, method_name, args, method_calls)
10
+ @class_name = class_name
11
+ @method_name = method_name
12
+ @args = args
13
+ @method_calls = method_calls
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,537 @@
1
+ require 'set'
2
+ require 'pry'
3
+
4
+ module Archruby
5
+ module Ruby
6
+ module TypeInference
7
+ module Ruby
8
+
9
+ class ParserForTypeinference < SexpInterpreter
10
+ attr_reader :dependencies, :method_definitions
11
+
12
+ def initialize
13
+ super()
14
+ @current_scope = LocalScope.new
15
+ @current_dependency_class = []
16
+ @current_dependency_class_name = nil
17
+ @current_class = nil
18
+ @module_names = []
19
+ @complete_class_name = []
20
+ @classes = []
21
+ @current_class = []
22
+ @method_definitions = []
23
+ @dependencies = []
24
+ end
25
+
26
+ def parse(content)
27
+ ast = ruby_parser.parse(content)
28
+ process(ast)
29
+ [@dependencies, @method_definitions]
30
+ end
31
+
32
+ def process_block(exp)
33
+ _, *args = exp
34
+ args.map! { |subtree| process(subtree) }
35
+ end
36
+
37
+ def process_lasgn(exp)
38
+ _, variable_name, *args = exp
39
+ args.map! { |subtree| process(subtree) }
40
+ if @current_dependency_class_name
41
+ @current_scope.add_variable(variable_name, @current_dependency_class_name)
42
+ end
43
+ @current_dependency_class_name = nil
44
+ end
45
+
46
+ def process_call(exp)
47
+ _, receiver, method_name, *args = exp
48
+ process(receiver)
49
+ args.map! { |subtree| process(subtree) }
50
+ end
51
+
52
+ def process_const(exp)
53
+ _, const_name = exp
54
+ if !@current_dependency_class.empty?
55
+ @current_dependency_class_name = build_full_name(const_name)
56
+ else
57
+ @current_dependency_class_name = const_name.to_s
58
+ end
59
+ add_dependencies(nil, nil, @current_dependency_class_name)
60
+ end
61
+
62
+ def process_colon2(exp)
63
+ _, first_part, last_part = exp
64
+ @current_dependency_class.unshift(last_part)
65
+ process(first_part)
66
+ end
67
+
68
+ def process_colon3(exp)
69
+ _, constant_name = exp
70
+ const_name = build_full_name("::#{constant_name}")
71
+ add_dependencies(nil, nil, const_name)
72
+ end
73
+
74
+ def build_full_name(const_name)
75
+ @current_dependency_class.unshift(const_name)
76
+ full_class_path = @current_dependency_class.join('::')
77
+ @current_dependency_class = []
78
+ full_class_path
79
+ end
80
+
81
+ def process_module(exp)
82
+ _, module_name, *args = exp
83
+ if module_name.class == Symbol
84
+ @module_names.push(module_name.to_s)
85
+ end
86
+ args.map! {|sub_tree| process(sub_tree)}
87
+ end
88
+
89
+ def process_class exp
90
+ _, class_name, *args = exp
91
+ if class_name.class == Symbol
92
+ if !@module_names.empty?
93
+ @classes << "#{@module_names.join("::")}::#{class_name}"
94
+ else
95
+ @classes << class_name.to_s
96
+ end
97
+ @current_class << @classes.last
98
+ else
99
+ # cai aqui quando a definicao é algo do tipo: class Teste::De end
100
+ get_complete_class_name class_name
101
+ @classes << @complete_class_name.join("::")
102
+ @complete_class_name = []
103
+ @current_class << @classes.last
104
+ end
105
+ args.map! {|sub_tree| process(sub_tree) if sub_tree.class == Sexp}
106
+ @current_class.pop
107
+ end
108
+
109
+ def get_complete_class_name exp
110
+ if exp[0] == :const
111
+ _, const_name = exp
112
+ @complete_class_name.unshift const_name
113
+ return
114
+ else
115
+ _, first_part, last_constant_part = exp
116
+ if ( _ == :colon3 )
117
+ process(exp)
118
+ else
119
+ @complete_class_name.unshift(last_constant_part)
120
+ get_complete_class_name first_part
121
+ end
122
+ end
123
+ end
124
+
125
+ def process_defn(exp)
126
+ _, method_name, method_arguments, *method_body = exp
127
+ @current_scope.add_new_scope
128
+ args = ProcessMethodArguments.new(method_arguments).parse
129
+ populate_scope_with_formal_parameters(args)
130
+ method_calls = ProcessMethodBody.new(method_body, @current_scope).parse
131
+ add_method_definition(method_name, args, method_calls)
132
+ add_dependencies(args, method_calls)
133
+ @current_scope.remove_scope
134
+ end
135
+
136
+ def process_defs(exp)
137
+ #transformando em um defn
138
+ without_node_type = exp[2..-1].to_a
139
+ without_node_type.unshift(:defn)
140
+ new_sexp = Sexp.from_array(without_node_type)
141
+ process_defn(new_sexp)
142
+ end
143
+
144
+ def add_method_definition(method_name, args, method_calls)
145
+ @method_definitions << MethodDefinition.new(
146
+ @classes.last,
147
+ method_name,
148
+ args,
149
+ method_calls
150
+ )
151
+ end
152
+
153
+ def add_dependencies(args = nil, method_calls = nil, class_name = nil)
154
+ return if @current_class.last.nil?
155
+ class_dependency = ClassDependency.new(@classes.last)
156
+ if class_name
157
+ class_dependency.add_dependency(class_name)
158
+ end
159
+ if args
160
+ args.each do |key, value|
161
+ if value.length > 0 #it is a set object
162
+ class_dependency.add_dependency(value.first)
163
+ end
164
+ end
165
+ end
166
+ if method_calls
167
+ method_calls.each do |method_call|
168
+ class_dependency.add_dependency(method_call.class_name)
169
+ end
170
+ end
171
+ @dependencies << class_dependency
172
+ end
173
+
174
+ def populate_scope_with_formal_parameters(args)
175
+ if !args.empty?
176
+ args.each do |key, value|
177
+ #value it is a set object
178
+ @current_scope.add_formal_parameter(key, value.first)
179
+ end
180
+ end
181
+ end
182
+
183
+ def process_lit(exp)
184
+ end
185
+
186
+ def process_cdecl(exp)
187
+ end
188
+
189
+ def process_if(exp)
190
+ _, *args = exp
191
+ args.map! {|sub_tree| process(sub_tree) if sub_tree.class == Sexp }
192
+ end
193
+
194
+ def process_array(exp)
195
+ _, *args = exp
196
+ args.map! {|sub_tree| process(sub_tree)}
197
+ end
198
+
199
+ def process_gvar(exp)
200
+ #global variables
201
+ end
202
+
203
+ def process_and(exp)
204
+ _, left_side, right_side = exp
205
+ process(left_side)
206
+ process(right_side)
207
+ end
208
+
209
+ def process_or(exp)
210
+ _, left_side, right_side = exp
211
+ process(left_side)
212
+ process(right_side)
213
+ end
214
+
215
+ def process_case(exp)
216
+ _, condition, when_part, ensure_part = exp
217
+ process(condition)
218
+ process(when_part)
219
+ process(ensure_part)
220
+ end
221
+
222
+ def process_when(exp)
223
+ _, condition, body = exp
224
+ process(condition)
225
+ process(body)
226
+ end
227
+
228
+ def process_rescue(exp)
229
+ _, body, rescbody = exp
230
+ process(body)
231
+ process(rescbody)
232
+ end
233
+
234
+ def process_dregx(exp)
235
+ _, str, *args = exp
236
+ args.map! {|sub_tree| process(sub_tree)}
237
+ end
238
+
239
+ def process_dregx_once(exp)
240
+ _, start, *args = exp
241
+ args.map! {|sub_tree| process(sub_tree) if sub_tree.class == Sexp}
242
+ end
243
+
244
+ def process_evstr(exp)
245
+ _, *args = exp
246
+ args.map! {|sub_tree| process(sub_tree)}
247
+ end
248
+
249
+ def process_dxstr(exp)
250
+ _, str, *args = exp
251
+ args.map! {|sub_tree| process(sub_tree)}
252
+ end
253
+
254
+ def process_resbody(exp)
255
+ _, body, resbody = exp
256
+ process(body)
257
+ process(resbody)
258
+ end
259
+
260
+ def process_ensure(exp)
261
+ _, *args = exp
262
+ args.map! {|sub_tree| process(sub_tree)}
263
+ end
264
+
265
+ def process_block_pass(exp)
266
+ _, *args = exp
267
+ args.map! {|sub_tree| process(sub_tree)}
268
+ end
269
+
270
+ def process_op_asgn2(exp)
271
+ _, receiver, method, met, last = exp
272
+ process(receiver)
273
+ process(last)
274
+ end
275
+
276
+ def process_return(exp)
277
+ end
278
+
279
+ def process_next(exp)
280
+ end
281
+
282
+ def process_alias(exp)
283
+ end
284
+
285
+ def process_ivar(exp)
286
+ _, var_name, *args = exp
287
+ args.map! {|sub_tree| process(sub_tree)}
288
+ end
289
+
290
+ def process_svalue(exp)
291
+ _, *args = exp
292
+ args.map! {|sub_tree| process(sub_tree)}
293
+ end
294
+
295
+ def process_not(exp)
296
+ _, *args = exp
297
+ args.map! {|sub_tree| process(sub_tree)}
298
+ end
299
+
300
+ def process_dot2(exp)
301
+ _, left, right = exp
302
+ process(left)
303
+ process(right)
304
+ end
305
+
306
+ def process_to_ary(exp)
307
+ _, *args = exp
308
+ args.map! {|sub_tree| process(sub_tree)}
309
+ end
310
+
311
+ def process_masgn(exp)
312
+ _, *args = exp
313
+ args.map! {|sub_tree| process(sub_tree)}
314
+ end
315
+
316
+ def process_match2(exp)
317
+ _, rec, *args = exp
318
+ args.map! {|sub_tree| process(sub_tree)}
319
+ end
320
+
321
+ def process_match3(exp)
322
+ _, first, second = exp
323
+ process(first)
324
+ process(second)
325
+ end
326
+
327
+ def process_while(exp)
328
+ _, condition, body = exp
329
+ process(condition)
330
+ process(body)
331
+ end
332
+
333
+ def process_until(exp)
334
+ _, condition, body = exp
335
+ process(condition)
336
+ process(body)
337
+ end
338
+
339
+ def process_for(exp)
340
+ _, x, y, body = exp
341
+ process(x)
342
+ process(y)
343
+ process(body)
344
+ end
345
+
346
+ def process_valias(exp)
347
+
348
+ end
349
+
350
+ def process_xstr(exp)
351
+ end
352
+
353
+ def process_lvar(exp)
354
+ #chamado para pegar o valor
355
+ end
356
+
357
+ def process_str(exp)
358
+ end
359
+
360
+ def process_begin(exp)
361
+ end
362
+
363
+ def process_retry(exp)
364
+ end
365
+
366
+ def process_cvdecl(exp)
367
+ _, instance_classvar_name, *value = exp
368
+ value.map! {|sub_tree| process(sub_tree)}
369
+ end
370
+
371
+ def process_defined(exp)
372
+ _, *args = exp
373
+ args.map! {|sub_tree| process(sub_tree)}
374
+ end
375
+
376
+ def process_postexe(exp)
377
+ end
378
+
379
+ def process_iasgn(exp)
380
+ _, instance_varialbe_name, *value = exp
381
+ value.map! { |subtree| process(subtree) }
382
+ end
383
+
384
+ def process_dsym(exp)
385
+ _, str, *args = exp
386
+ args.map! {|sub_tree| process(sub_tree)}
387
+ end
388
+
389
+ def process_undef(exp)
390
+ end
391
+
392
+ def process_super(exp)
393
+ end
394
+
395
+ def process_attrasgn(exp)
396
+ _, receiver, method, arg, value = exp
397
+ process(receiver)
398
+ process(value)
399
+ end
400
+
401
+ def process_splat(exp)
402
+ _, *args = exp
403
+ args.map! {|sub_tree| process(sub_tree)}
404
+ end
405
+
406
+ def process_iter(exp)
407
+ _, first_part, second_part, *body = exp
408
+ process(first_part)
409
+ body.map! {|sub_tree| process(sub_tree)}
410
+ end
411
+
412
+ def process_sclass(exp)
413
+ _, singleton_class, *body = exp
414
+ body.map! {|sub_tree| process(sub_tree)}
415
+ end
416
+
417
+ def process_hash(exp)
418
+ _, key, value = exp
419
+ process(key)
420
+ process(value)
421
+ end
422
+
423
+ def process_op_asgn1(exp)
424
+ _, receiver, arg, method_name, *args = exp
425
+ args.map! {|sub_tree| process(sub_tree)}
426
+ end
427
+
428
+ def process_op_asgn_or(exp)
429
+ _, *args = exp
430
+ args.map! {|sub_tree| process(sub_tree)}
431
+ end
432
+
433
+ def process_gasgn(exp)
434
+ _, global_var_name, *value = exp
435
+ value.map! {|sub_tree| process(sub_tree)}
436
+ end
437
+
438
+ def process_cvar(exp)
439
+ # class variable
440
+ end
441
+
442
+ def process_break(exp)
443
+ end
444
+
445
+ def process_nth_ref(exp)
446
+ end
447
+
448
+ def process_dstr(exp)
449
+ # string dinamica, pode ser interessante se
450
+ # quisermos pegar alguma coisa dentro delas
451
+
452
+ end
453
+
454
+ def process_true(exp)
455
+ end
456
+
457
+ def process_false(exp)
458
+ end
459
+
460
+ def process_self(exp)
461
+ end
462
+
463
+ def process_nil(exp)
464
+ end
465
+
466
+ # def process_sclass(exp)
467
+ # binding.pry
468
+ # end
469
+
470
+ def ruby_parser
471
+ RubyParser.new
472
+ end
473
+ end
474
+
475
+ class LocalScope
476
+ def initialize
477
+ @scopes = [Set.new]
478
+ @formal_parameters = [Set.new]
479
+ @current_scope = @scopes.last
480
+ @current_formal_parameters = @formal_parameters.last
481
+ end
482
+
483
+ def add_variable(name, type)
484
+ @current_scope.add([name, type])
485
+ end
486
+
487
+ def add_formal_parameter(name, type)
488
+ @current_formal_parameters.add([name, type])
489
+ end
490
+
491
+ def var_type(name)
492
+ @current_scope.each do |var_info|
493
+ if var_info[0].to_s == name.to_s
494
+ return var_info[1]
495
+ end
496
+ end
497
+ return nil
498
+ end
499
+
500
+ def has_formal_parameter(name)
501
+ check_from_collection(@current_formal_parameters, name)
502
+ end
503
+
504
+ def has_local_params(name)
505
+ check_from_collection(@current_scope, name)
506
+ end
507
+
508
+ def add_new_scope
509
+ @scopes << Set.new
510
+ @current_scope = @scopes.last
511
+ @formal_parameters << Set.new
512
+ @current_formal_parameters = @formal_parameters.last
513
+ end
514
+
515
+ def remove_scope
516
+ @scopes.pop
517
+ @current_scope = @scopes.last
518
+ @formal_parameters.pop
519
+ @current_formal_parameters = @formal_parameters.last
520
+ end
521
+
522
+ private
523
+
524
+ def check_from_collection(collection, name)
525
+ collection.each do |var_info|
526
+ if var_info[0].to_s == name.to_s
527
+ return true
528
+ end
529
+ end
530
+ return false
531
+ end
532
+ end
533
+
534
+ end
535
+ end
536
+ end
537
+ end