yadriggy 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,10 @@ require 'yadriggy/ruby_typecheck'
5
5
 
6
6
  module Yadriggy
7
7
 
8
- # Type checker for Ruby with type inference.
8
+ # Type checker for Ruby with type inference. The type system is slightly
9
+ # different from Ruby's one.
10
+ # For example, once an integer is assigned to a local variable,
11
+ # assigning a String value to it causes a type error.
9
12
  #
10
13
  class RubyTypeInferer < RubyTypeChecker
11
14
 
@@ -29,7 +32,7 @@ module Yadriggy
29
32
  end
30
33
  end
31
34
 
32
- # @private
35
+ # @api private
33
36
  # When the initial value of a variable is an InstanceType,
34
37
  # the type of the variable has to be a RubyCass type
35
38
  # corresponding to that instance type.
@@ -48,9 +51,47 @@ module Yadriggy
48
51
  # Note that obj.name += ... is regarded as obj.name=(obj.name + ...).
49
52
  #
50
53
  rule(Assign) do
51
- rtype = type(ast.right)
52
- left_expr = ast.left
53
- if ast.op != :'=' # if op is += etc.
54
+ if ast.left.is_a?(Array)
55
+ type_multi_assign(ast.left, ast.op, ast.right, ast.parent)
56
+ else
57
+ ast_right = ast.right
58
+ if ast_right.is_a?(Array)
59
+ ast_right.each {|e| type(e) }
60
+ type_assign(ast.left, ast.op, ast_right, DynType, ast.parent)
61
+ else
62
+ rtype = type(ast_right)
63
+ type_assign(ast.left, ast.op, ast_right, rtype, ast.parent)
64
+ end
65
+ end
66
+ end
67
+
68
+ # @api private
69
+ # Overriding and overloading
70
+ def type_multi_assign(ast_left, ast_op, ast_right, ast_parent)
71
+ if ast_right.is_a?(Array)
72
+ rtypes = ast_right.map {|e| type(e) }
73
+ ast_left.each_with_index do |v, i|
74
+ if i < rtypes.size
75
+ type_assign(v, ast_op, ast_right[i], rtypes[i], ast_parent)
76
+ else
77
+ # passing "ast_right" is wrong but it will not be used..
78
+ type_assign(v, ast_op, ast_right, RubyClass::NilClass, ast_parent)
79
+ end
80
+ end
81
+ else
82
+ type(ast_right)
83
+ ast_left.each_with_index do |v, i|
84
+ # passing "ast_right" is wrong but it will not be used..
85
+ type_assign(v, ast_op, ast_right, DynType, ast_parent)
86
+ end
87
+ end
88
+ DynType
89
+ end
90
+
91
+ # @api private
92
+ # Overriding and overloading.
93
+ def type_assign(left_expr, ast_op, right_expr, rtype, ast_parent)
94
+ if ast_op != :'=' # if op is += etc.
54
95
  ltype = type(left_expr)
55
96
  LocalVarType.role(ltype)&.definition = left_expr
56
97
  ltype
@@ -58,9 +99,11 @@ module Yadriggy
58
99
  vtype = type_env.bound_name?(left_expr)
59
100
  if vtype.nil? # if a new name is found,
60
101
  method_name = left_expr.name + '='
61
- if is_attr_accessor?(ast, type_env, method_name.to_sym) # self.name=()?
62
- call_expr = Call.make(name: method_name, args: [ ast.right ])
63
- get_call_expr_type(call_expr, type_env, method_name)
102
+ if is_attr_accessor?(type_env, method_name.to_sym) # self.name=()?
103
+ call_expr = Call.make(name: method_name, args: [ right_expr ],
104
+ parent: ast_parent)
105
+ get_call_expr_type_with_argtypes(call_expr, type_env, method_name,
106
+ [ rtype ])
64
107
  else
65
108
  bind_local_var(type_env, left_expr, rtype)
66
109
  end
@@ -72,9 +115,10 @@ module Yadriggy
72
115
  elsif left_expr.is_a?(Call) && left_expr.op == :'.' # obj.name=()?
73
116
  method_name = left_expr.name.name + '='
74
117
  call_expr = Call.make(receiver: left_expr.receiver,
75
- name: method_name, args: [ ast.right ],
76
- parent: ast.parent)
77
- get_call_expr_type(call_expr, type_env, method_name)
118
+ name: method_name, args: [ right_expr ],
119
+ parent: ast_parent)
120
+ get_call_expr_type_with_argtypes(call_expr, type_env, method_name,
121
+ [ rtype ])
78
122
  elsif left_expr.is_a?(InstanceVariable) # @var = ..., @@cvar = ..., @var += ...
79
123
  get_instance_variable_type(type_env.context, left_expr, true,
80
124
  InstanceType.role(rtype)&.supertype || rtype)
@@ -86,8 +130,8 @@ module Yadriggy
86
130
  end
87
131
  end
88
132
 
89
- # @private
90
- def is_attr_accessor?(expr, tenv, name)
133
+ # @api private
134
+ def is_attr_accessor?(tenv, name)
91
135
  self_t = type_env.context
92
136
  !self_t.nil? &&
93
137
  (self_t.method_defined?(name) ||
@@ -102,7 +146,7 @@ module Yadriggy
102
146
  type
103
147
  else
104
148
  method_name = name_ast.to_sym
105
- if is_attr_accessor?(name_ast, tenv, method_name.to_sym)
149
+ if is_attr_accessor?(tenv, method_name.to_sym)
106
150
  call_expr = Call.make(name: method_name, parent: name_ast.parent)
107
151
  get_call_expr_type(call_expr, tenv, method_name)
108
152
  else
@@ -117,15 +161,6 @@ module Yadriggy
117
161
  end
118
162
  end
119
163
 
120
- rule(Const) do
121
- v = ast.value
122
- if v == Undef
123
- DynType
124
- else
125
- InstanceType.new(v)
126
- end
127
- end
128
-
129
164
  rule(GlobalVariable) do
130
165
  v = ast.value
131
166
  if v == Undef
@@ -191,7 +226,7 @@ module Yadriggy
191
226
  binary_type(ast, right_t, left_t)
192
227
  end
193
228
 
194
- # @private
229
+ # @api private
195
230
  def binary_type(bin_expr, right_t, left_t)
196
231
  op = bin_expr.op
197
232
  case op
@@ -263,7 +298,7 @@ module Yadriggy
263
298
  end
264
299
  end
265
300
 
266
- # @private
301
+ # @api private
267
302
  # Overrides {RubyTypeChecker#get_return_type}.
268
303
  #
269
304
  def get_return_type(an_ast, mthd, new_tenv, arg_types)
@@ -3,8 +3,26 @@
3
3
  require 'ripper'
4
4
  require 'pry'
5
5
 
6
+ class Pry
7
+ class History
8
+ # @api private
9
+ # We modify Pry::History::push to record a duplicated line as well.
10
+ def push(line)
11
+ unless line.empty? || line.include?("\0")
12
+ @pusher.call(line)
13
+ @history << line
14
+ if !should_ignore?(line) && Pry.config.history.should_save
15
+ @saver.call(line)
16
+ end
17
+ end
18
+ line
19
+ end
20
+ alias << push
21
+ end
22
+ end
23
+
6
24
  module Yadriggy
7
- # @private
25
+ # @api private
8
26
  # Retrieves source code in the S-expression style.
9
27
  class SourceCode
10
28
 
@@ -24,24 +42,15 @@ module Yadriggy
24
42
 
25
43
  def self.read_pry_history
26
44
  cmds = Pry.commands
27
-
28
- # The line number seems wrong if the source code is in pry_history.
29
- # To correct the line number, a blank line is added to source.
30
- source = "\n"
31
-
32
- lineno = 0
33
- lineno1 = Pry.history.original_lines
34
- File.foreach(Pry.config.history.file) do |line|
35
- lineno += 1
36
- if lineno > lineno1
37
- if cmds.select {|k,v| v.matches?(line) }.empty?
38
- source << line
39
- else
40
- # source << "\n"
41
- end
45
+ his = Pry.history.to_a[Pry.history.original_lines ...
46
+ Pry.history.history_line_count]
47
+ his.reduce('') do |source, line|
48
+ if cmds.select {|k,v| v.matches?(line) }.empty?
49
+ source << line << "\n"
50
+ else
51
+ source # << "\n"
42
52
  end
43
53
  end
44
- source
45
54
  end
46
55
 
47
56
  def self.min(a, b)
@@ -105,6 +114,7 @@ module Yadriggy
105
114
  prog[1][2][0] == line
106
115
  end
107
116
 
117
+ # @api private
108
118
  class Cons
109
119
  include Enumerable
110
120
  attr_accessor :head, :tail
@@ -21,7 +21,9 @@ module Yadriggy
21
21
  # also defined in ast.rb
22
22
 
23
23
  # The user type (or non-terminal symbol) corresponding
24
- # to this node.
24
+ # to this node. This is effective only after checking syntax
25
+ # by {Syntax#check}.
26
+ #
25
27
  # @return [Symbol|nil] the user type.
26
28
  attr_accessor :usertype
27
29
  end
@@ -65,7 +67,7 @@ module Yadriggy
65
67
  update_hash(ast.tree.body)
66
68
  end
67
69
 
68
- # @private
70
+ # @api private
69
71
  # @param [Body] body
70
72
  # @return [void]
71
73
  def update_hash(body)
@@ -402,13 +404,25 @@ module Yadriggy
402
404
  # For example, `Binary = { op: :+ }` specifies that the `op` property
403
405
  # of `Binary` has to be `:+`. Note that the other properties such
404
406
  # as `left` and `right` are not checked. Hence, when the rules are
405
- # <pre>Binary = { op: :+ }
406
- # Unary = { op: :! }</pre>
407
+ #
408
+ # Binary = { op: :+ }
409
+ # Unary = { op: :! }
410
+ #
407
411
  # `a + -b` causes no syntax error since the unary expression `-b` is
408
412
  # the right operand of the binary expression. The rule for {Binary}
409
413
  # is passed and hence the rule for {Unary} is not applied to `-b`.
410
- # <pre>Binary = { op: :+, right: Unary }
411
- # Unary = { op: :! }</pre>
414
+ #
415
+ # Binary = { op: :+, right: Unary }
416
+ # Unary = { op: :! }
417
+ #
418
+ # If the right-hand side of = (or <=) is `nil`, then the rule always
419
+ # fails. For example,
420
+ #
421
+ # SymbolLiteral = nil
422
+ #
423
+ # This specifies that any `SymbolLiteral` node does not pass syntax
424
+ # checking. If the right-hand side is `{}`, any `SymbolLiteral` passes
425
+ # without further checking.
412
426
  #
413
427
  # An AST subtree passes syntax checking if no rule is found for that
414
428
  # subtree.
@@ -475,7 +489,8 @@ module Yadriggy
475
489
  Binary <= { left: expr, op: Symbol, right: expr }
476
490
  ArrayRef <= { array: expr, indexes: [ expr ] }
477
491
  ArrayRefField <= ArrayRef
478
- Assign <= Binary
492
+ Assign <= { left: [expr] | expr, op: Symbol,
493
+ right: [expr] | expr }
479
494
  Dots <= Binary
480
495
  Unary <= { op: Symbol, operand: expr }
481
496
  ConstPathRef <= { scope: (ConstPathRef | Const), name: Const }
@@ -499,7 +514,7 @@ module Yadriggy
499
514
  block_param: (Identifier) }
500
515
  Block <= Parameters + { body: exprs }
501
516
  Lambda <= Block
502
- Call <= { receiver: (expr), op: (Symbol), name: Identifier,
517
+ Call <= { receiver: (expr), op: (Symbol), name: (Identifier),
503
518
  args: [ expr ], block_arg: (expr), block: (Block) }
504
519
  Command <= Call
505
520
  Exprs <= { expressions: [ expr ] }
@@ -8,7 +8,7 @@ module Yadriggy
8
8
  # Don't use `is_a?` but use `has_role?` or `role`.
9
9
  #
10
10
  class Type
11
- # @private
11
+ # @api private
12
12
  def self.get_instance_method_object(recv_type, method_name)
13
13
  recv_type.get_method_object(method_name)
14
14
  end
@@ -54,7 +54,7 @@ module Yadriggy
54
54
  self == t
55
55
  end
56
56
 
57
- # @private
57
+ # @api private
58
58
  # Only {DynType}, {UnionType} and {OptionalRole} override this method.
59
59
  def is_super_of? (t)
60
60
  false
@@ -96,7 +96,7 @@ module Yadriggy
96
96
  DynType
97
97
  end
98
98
 
99
- # @private
99
+ # @api private
100
100
  # Gets a method with the given name declared in this type.
101
101
  # `nil` is returned when the method is not exactly determined.
102
102
  #
@@ -111,7 +111,7 @@ module Yadriggy
111
111
  end
112
112
  end
113
113
 
114
- # @private
114
+ # @api private
115
115
  class NonRubyType < Type
116
116
  def initialize(obj_name, type_name)
117
117
  @obj_name = obj_name
@@ -152,7 +152,7 @@ module Yadriggy
152
152
  # Dynamic type.
153
153
  DynType = NonRubyType.new('#<Yadriggy::DynType>', 'DynType')
154
154
 
155
- # @private
155
+ # @api private
156
156
  def DynType.is_super_of?(t)
157
157
  true
158
158
  end
@@ -198,17 +198,17 @@ module Yadriggy
198
198
  (normalize(self) | normalize(ut)).size <= @types.size
199
199
  end
200
200
 
201
- # @private
201
+ # @api private
202
202
  def normalize(utype)
203
203
  utype.types.map {|e| e.copy(OptionalRole) }
204
204
  end
205
205
 
206
- # @private
206
+ # @api private
207
207
  def hash
208
208
  @types.hash
209
209
  end
210
210
 
211
- # @private
211
+ # @api private
212
212
  # Check the subtype relation.
213
213
  # @param [Type] t the other type.
214
214
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -244,17 +244,17 @@ module Yadriggy
244
244
  @type = t
245
245
  end
246
246
 
247
- # @private
247
+ # @api private
248
248
  def == (t)
249
249
  CommonSuperType.role(t)&.type == @type
250
250
  end
251
251
 
252
- # @private
252
+ # @api private
253
253
  def hash
254
254
  @type.hash + 1
255
255
  end
256
256
 
257
- # @private
257
+ # @api private
258
258
  # Check the subtype relation.
259
259
  # @param [Type] t the other type.
260
260
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -268,7 +268,7 @@ module Yadriggy
268
268
  end
269
269
  end
270
270
 
271
- # @private
271
+ # @api private
272
272
  def get_method_object(method_name)
273
273
  nil
274
274
  end
@@ -295,17 +295,17 @@ module Yadriggy
295
295
  # by {CommonSuperType}.
296
296
  #
297
297
  class RubyClass < Type
298
- # @private
298
+ # @api private
299
299
  Table = {}
300
300
 
301
- # @private
301
+ # @api private
302
302
  def self.make(clazz)
303
303
  obj = RubyClass.new(clazz)
304
304
  Table[clazz] = obj
305
305
  obj
306
306
  end
307
307
 
308
- # @private
308
+ # @api private
309
309
  def self.set_alias(clazz, ruby_class)
310
310
  Table[clazz] = ruby_class
311
311
  end
@@ -330,12 +330,12 @@ module Yadriggy
330
330
  RubyClass.role(t)&.exact_type == @ruby_class
331
331
  end
332
332
 
333
- # @private
333
+ # @api private
334
334
  def hash
335
335
  @ruby_class.hash
336
336
  end
337
337
 
338
- # @private
338
+ # @api private
339
339
  # Check the subtype relation.
340
340
  # @param [Type] t the other type.
341
341
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -353,14 +353,14 @@ module Yadriggy
353
353
  end
354
354
  end
355
355
 
356
- # @private
356
+ # @api private
357
357
  def get_method_object(method_name)
358
358
  @ruby_class.instance_method(method_name)
359
359
  rescue NameError
360
360
  Type.error_found!("no such method: #{@ruby_class}\##{method_name}")
361
361
  end
362
362
 
363
- # @private
363
+ # @api private
364
364
  def exact_type
365
365
  @ruby_class
366
366
  end
@@ -417,17 +417,17 @@ module Yadriggy
417
417
  @object = obj
418
418
  end
419
419
 
420
- # @private
420
+ # @api private
421
421
  def == (t)
422
422
  InstanceType.role(t)&.object == @object
423
423
  end
424
424
 
425
- # @private
425
+ # @api private
426
426
  def hash
427
427
  @object.hash
428
428
  end
429
429
 
430
- # @private
430
+ # @api private
431
431
  # Check the subtype relation.
432
432
  # @param [Type] t the other type.
433
433
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -442,13 +442,13 @@ module Yadriggy
442
442
  end
443
443
  end
444
444
 
445
- # @private
445
+ # @api private
446
446
  # Recall that `1.class` was `Fixnum` in Ruby earlier than 2.4.
447
447
  def exact_type
448
448
  @object.is_a?(Integer) ? Integer : @object.class
449
449
  end
450
450
 
451
- # @private
451
+ # @api private
452
452
  def get_method_object(method_name)
453
453
  @object.method(method_name)
454
454
  rescue NameError
@@ -500,18 +500,18 @@ module Yadriggy
500
500
  # @return [Parameters] the method definition.
501
501
  def method_def() @method_def end
502
502
 
503
- # @private
503
+ # @api private
504
504
  def == (t)
505
505
  mt = MethodType.role(t)
506
506
  !mt.nil? && @result_type == mt.result_type && @param_types == mt.params
507
507
  end
508
508
 
509
- # @private
509
+ # @api private
510
510
  def hash
511
511
  @result_type.hash + @param_types.hash
512
512
  end
513
513
 
514
- # @private
514
+ # @api private
515
515
  def <= (t)
516
516
  if t.is_super_of?(self)
517
517
  true
@@ -584,12 +584,12 @@ module Yadriggy
584
584
  ct.args == @args
585
585
  end
586
586
 
587
- # @private
587
+ # @api private
588
588
  def hash
589
589
  @ruby_class.hash + @args.reduce(0) {|h,p| h + p.hash }
590
590
  end
591
591
 
592
- # @private
592
+ # @api private
593
593
  # Check the subtype relation.
594
594
  # @param [Type] t the other type.
595
595
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -608,7 +608,7 @@ module Yadriggy
608
608
  end
609
609
  end
610
610
 
611
- # @private
611
+ # @api private
612
612
  def exact_type
613
613
  @ruby_class
614
614
  end
@@ -636,7 +636,7 @@ module Yadriggy
636
636
  @type = type
637
637
  end
638
638
 
639
- # @private
639
+ # @api private
640
640
  def copy(without_role)
641
641
  chain = @type.copy(without_role)
642
642
  if self.is_a?(without_role)
@@ -652,7 +652,7 @@ module Yadriggy
652
652
  end
653
653
  end
654
654
 
655
- # @private
655
+ # @api private
656
656
  def update_type(t)
657
657
  @type = t
658
658
  end
@@ -663,22 +663,22 @@ module Yadriggy
663
663
  @type == t
664
664
  end
665
665
 
666
- # @private
666
+ # @api private
667
667
  def hash
668
668
  @type.hash
669
669
  end
670
670
 
671
- # @private
671
+ # @api private
672
672
  def <= (t)
673
673
  @type <= t
674
674
  end
675
675
 
676
- # @private
676
+ # @api private
677
677
  def is_super_of?(t)
678
678
  @type.is_super_of?(t)
679
679
  end
680
680
 
681
- # @private
681
+ # @api private
682
682
  def has_role?(a_role)
683
683
  if self.is_a?(a_role)
684
684
  self
@@ -687,12 +687,12 @@ module Yadriggy
687
687
  end
688
688
  end
689
689
 
690
- # @private
690
+ # @api private
691
691
  def exact_type
692
692
  @type.exact_type
693
693
  end
694
694
 
695
- # @private
695
+ # @api private
696
696
  def get_method_object(method_name)
697
697
  Type.get_instance_method_object(@type, method_name)
698
698
  end