archruby 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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