rdl 2.1.0 → 2.2.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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
data/rdl.gemspec
CHANGED
@@ -4,19 +4,20 @@
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'rdl'
|
7
|
-
s.version = '2.
|
8
|
-
s.date = '
|
7
|
+
s.version = '2.2.0'
|
8
|
+
s.date = '2019-06-09'
|
9
9
|
s.summary = 'Ruby type and contract system'
|
10
10
|
s.description = <<-EOF
|
11
11
|
RDL is a gem that adds types and contracts to Ruby. RDL includes extensive
|
12
12
|
support for specifying method types, which can either be enforced as
|
13
13
|
contracts or statically checked.
|
14
14
|
EOF
|
15
|
-
s.authors = ['Jeffrey S. Foster', 'Brianna M. Ren', 'T. Stephen Strickland', 'Alexander T. Yu', 'Milod Kazerounian']
|
15
|
+
s.authors = ['Jeffrey S. Foster', 'Brianna M. Ren', 'T. Stephen Strickland', 'Alexander T. Yu', 'Milod Kazerounian', 'Sankha Narayan Guria']
|
16
16
|
s.email = ['rdl-users@googlegroups.com']
|
17
17
|
s.files = `git ls-files`.split($/)
|
18
18
|
s.executables << 'rdl_query'
|
19
|
-
s.homepage = 'https://github.com/
|
19
|
+
s.homepage = 'https://github.com/tupl-tufts/rdl'
|
20
20
|
s.license = 'BSD-3-Clause'
|
21
21
|
s.add_runtime_dependency 'parser', '~>2.3', '>= 2.3.1.4'
|
22
|
+
s.add_runtime_dependency 'sql-parser', '~>0.0.2'
|
22
23
|
end
|
data/test/test_alias.rb
CHANGED
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
$LOAD_PATH << File.dirname(__FILE__) + "/../lib"
|
3
|
+
require 'rdl'
|
4
|
+
require 'types/core'
|
5
|
+
|
6
|
+
class TestArrayTypes < Minitest::Test
|
7
|
+
extend RDL::Annotate
|
8
|
+
|
9
|
+
def setup
|
10
|
+
RDL.reset
|
11
|
+
RDL.readd_comp_types
|
12
|
+
RDL.type_params :Array, [:t], :all? unless RDL::Globals.type_params["Array"]
|
13
|
+
RDL.type_params(:Range, [:t], nil, variance: [:+]) { |t| t.member?(self.begin) && t.member?(self.end) } unless RDL::Globals.type_params["Range"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_array_methods
|
17
|
+
self.class.class_eval {
|
18
|
+
type '() -> 2', typecheck: :now
|
19
|
+
def access_test1()
|
20
|
+
x = [1, 2, 3]
|
21
|
+
x[1]
|
22
|
+
end
|
23
|
+
|
24
|
+
type '(Array<Integer>) -> Integer', typecheck: :now
|
25
|
+
def access_test2(x)
|
26
|
+
x[5]
|
27
|
+
end
|
28
|
+
|
29
|
+
type '([2, String, Float], Integer) -> Integer or String or Float', typecheck: :now
|
30
|
+
def access_test3(x, y)
|
31
|
+
x[y]
|
32
|
+
end
|
33
|
+
|
34
|
+
type '(Array) -> t', typecheck: :now
|
35
|
+
def access_test4(a)
|
36
|
+
a[3]
|
37
|
+
end
|
38
|
+
|
39
|
+
type '([1,2,3]) -> Array<3 or 2 or 1>', typecheck: :now
|
40
|
+
def access_test5(a)
|
41
|
+
a[1..4]
|
42
|
+
end
|
43
|
+
|
44
|
+
type '(Array<Integer>) -> Array<Integer>', typecheck: :now
|
45
|
+
def access_test6(a)
|
46
|
+
a[1..4]
|
47
|
+
end
|
48
|
+
|
49
|
+
type '([1,2,3]) -> [2,3]', typecheck: :now
|
50
|
+
def access_test7(a)
|
51
|
+
a[1, 9]
|
52
|
+
end
|
53
|
+
|
54
|
+
type '([1,2,3]) -> 2', typecheck: :now
|
55
|
+
def fetch_test1(a)
|
56
|
+
a[1]
|
57
|
+
end
|
58
|
+
|
59
|
+
type '([1,2,3]) -> 1', typecheck: :now
|
60
|
+
def first_test1(a)
|
61
|
+
a.first
|
62
|
+
end
|
63
|
+
|
64
|
+
type '([1,2,3], Integer) -> Array<3 or 2 or 1>', typecheck: :now
|
65
|
+
def first_test2(a, i)
|
66
|
+
a.first(i)
|
67
|
+
end
|
68
|
+
|
69
|
+
type '() -> [1,2,3,1,2,3]', typecheck: :now
|
70
|
+
def mult_test1
|
71
|
+
[1,2,3]*2
|
72
|
+
end
|
73
|
+
|
74
|
+
type '([1,2,3], Integer) -> Array<3 or 2 or 1>', typecheck: :now
|
75
|
+
def mult_test2(a, i)
|
76
|
+
a * i
|
77
|
+
end
|
78
|
+
|
79
|
+
type '([1,2,3]) -> 3', typecheck: :now
|
80
|
+
def count_test1(a)
|
81
|
+
a.count
|
82
|
+
end
|
83
|
+
|
84
|
+
type '(Array<Integer>) -> Integer', typecheck: :now
|
85
|
+
def count_test2(a)
|
86
|
+
a.count
|
87
|
+
end
|
88
|
+
|
89
|
+
type '([1,2,3]) -> [1,2,3,4]', typecheck: :now
|
90
|
+
def append_test1(a)
|
91
|
+
a << 4
|
92
|
+
end
|
93
|
+
|
94
|
+
type '(Array<Integer>) -> Array<Integer>', typecheck: :now
|
95
|
+
def append_test2(a)
|
96
|
+
a << 5
|
97
|
+
end
|
98
|
+
|
99
|
+
type '([1,2,3]) -> [1,2,3,4,5]', typecheck: :now
|
100
|
+
def push_test1(a)
|
101
|
+
a.push(4, 5)
|
102
|
+
end
|
103
|
+
|
104
|
+
type :append_callee, '([1,2,3]) -> %bool'
|
105
|
+
|
106
|
+
type '([1,2,3]) -> %bool', typecheck: :append_fail1
|
107
|
+
def append_fail_test1(a)
|
108
|
+
append_callee(a)
|
109
|
+
a << 4
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
type '([1,2,3], [4,5,String]) -> [1,2,3,4,5,String]', typecheck: :now
|
114
|
+
def plus_test1(x, y)
|
115
|
+
x+y
|
116
|
+
end
|
117
|
+
|
118
|
+
type '([Integer,Integer,Integer], Array<String>) -> Array<Integer or String>', typecheck: :now
|
119
|
+
def plus_test2(x, y)
|
120
|
+
x+y
|
121
|
+
end
|
122
|
+
|
123
|
+
type '([1,2,3], Array) -> Array', typecheck: :now
|
124
|
+
def plus_test3(x, y)
|
125
|
+
x + y
|
126
|
+
end
|
127
|
+
|
128
|
+
type '(Array<String>, Array) -> Array', typecheck: :now
|
129
|
+
def plus_test4(x, y)
|
130
|
+
x + y
|
131
|
+
end
|
132
|
+
|
133
|
+
type '([1,2,3]) -> true', typecheck: :now
|
134
|
+
def include_test1(x)
|
135
|
+
x.include?(1)
|
136
|
+
end
|
137
|
+
|
138
|
+
type '([1,2,Integer]) -> %bool', typecheck: :now
|
139
|
+
def include_test2(x)
|
140
|
+
x.include?(4)
|
141
|
+
end
|
142
|
+
|
143
|
+
type '(Array<Integer>) -> %bool', typecheck: :now
|
144
|
+
def include_test3(x)
|
145
|
+
x.include?(42)
|
146
|
+
end
|
147
|
+
|
148
|
+
type '([1,2,3]) -> 2', typecheck: :now
|
149
|
+
def slice_test1(x)
|
150
|
+
x.slice(1)
|
151
|
+
end
|
152
|
+
|
153
|
+
type '([1,2,3]) -> [2,3]', typecheck: :now
|
154
|
+
def slice_test2(x)
|
155
|
+
x.slice(1,2)
|
156
|
+
end
|
157
|
+
|
158
|
+
type '([1,2,3]) -> [1,4 or 2,3]', typecheck: :now
|
159
|
+
def assign_test1(x)
|
160
|
+
x[1] = 4
|
161
|
+
x
|
162
|
+
end
|
163
|
+
|
164
|
+
type '(Integer, [1,2,3]) -> Array<1 or 2 or 3 or String>', typecheck: :now
|
165
|
+
def assign_test2(i, x)
|
166
|
+
x[i] = "hi"
|
167
|
+
x
|
168
|
+
end
|
169
|
+
|
170
|
+
type '(Array<Integer>) -> Array<Integer>', typecheck: :now
|
171
|
+
def assign_test3(x)
|
172
|
+
x[1] = 2
|
173
|
+
x
|
174
|
+
end
|
175
|
+
|
176
|
+
type :assign_callee, '([1,2,3]) -> %bool'
|
177
|
+
|
178
|
+
type '([1,2,3]) -> 2', typecheck: :now
|
179
|
+
def assign_test4(x)
|
180
|
+
assign_callee(x)
|
181
|
+
x[1] = 2
|
182
|
+
end
|
183
|
+
|
184
|
+
type '([1,2,3]) -> %any', typecheck: :assign_fail1
|
185
|
+
def assign_fail_test1(x)
|
186
|
+
assign_callee
|
187
|
+
x[1] = 100
|
188
|
+
end
|
189
|
+
|
190
|
+
type '(Array<Integer>) -> %any', typecheck: :assign_fail2
|
191
|
+
def assign_fail_test2(x)
|
192
|
+
x[1] = "hi"
|
193
|
+
end
|
194
|
+
|
195
|
+
type '([1,2,3]) -> false', typecheck: :now
|
196
|
+
def empty_test1(x)
|
197
|
+
x.empty?
|
198
|
+
end
|
199
|
+
|
200
|
+
type '(Array<Integer>) -> %bool', typecheck: :now
|
201
|
+
def empty_test2(x)
|
202
|
+
x.empty?
|
203
|
+
end
|
204
|
+
|
205
|
+
type '([1,2,3]) -> 1', typecheck: :now
|
206
|
+
def index_test1(x)
|
207
|
+
x.index(2)
|
208
|
+
end
|
209
|
+
|
210
|
+
type '([1,2,3], Integer) -> Integer', typecheck: :now
|
211
|
+
def index_test2(x, y)
|
212
|
+
x.index(y)
|
213
|
+
end
|
214
|
+
|
215
|
+
type '([1,Integer,3], 2) -> Integer', typecheck: :now
|
216
|
+
def index_test3(x, y)
|
217
|
+
x.index(y)
|
218
|
+
end
|
219
|
+
|
220
|
+
type '([1,2,3]) -> [3,2,1]', typecheck: :now
|
221
|
+
def reverse_test1(x)
|
222
|
+
x.reverse
|
223
|
+
end
|
224
|
+
|
225
|
+
type '([1,2,3]) -> [1,2,3]', typecheck: :now
|
226
|
+
def reverse_test2(x)
|
227
|
+
x.reverse
|
228
|
+
x
|
229
|
+
end
|
230
|
+
|
231
|
+
type '([1,2,3]) -> [3 or 1,2,1 or 3]', typecheck: :now
|
232
|
+
def reverse_test3(x)
|
233
|
+
x.reverse!
|
234
|
+
x
|
235
|
+
end
|
236
|
+
}
|
237
|
+
|
238
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :append_fail1 }
|
239
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :assign_fail1 }
|
240
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :assign_fail2 }
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
$LOAD_PATH << File.dirname(__FILE__) + "/../lib"
|
3
|
+
require 'rdl'
|
4
|
+
require 'types/core'
|
5
|
+
|
6
|
+
|
7
|
+
class TestBoundTypes < Minitest::Test
|
8
|
+
extend RDL::Annotate
|
9
|
+
|
10
|
+
def test_bound_types
|
11
|
+
self.class.class_eval {
|
12
|
+
type :uses_bound, "(t<::Integer) -> ``if t.is_a?(RDL::Type::SingletonType) then RDL::Globals.types[:integer] else RDL::Globals.types[:string] end``"
|
13
|
+
|
14
|
+
type :uses_bound_twice, "(t<::Integer, p<::Integer) -> ``if t==p then RDL::Globals.types[:integer] else RDL::Globals.types[:string] end``"
|
15
|
+
|
16
|
+
type :uses_optional, "(?String, t<::Integer) -> ``if t.is_a?(RDL::Type::SingletonType) then RDL::Globals.types[:integer] else RDL::Globals.types[:string] end``"
|
17
|
+
|
18
|
+
type "(Integer) -> String", typecheck: :now
|
19
|
+
def calls_bound1(x)
|
20
|
+
uses_bound(x)
|
21
|
+
end
|
22
|
+
|
23
|
+
type "(1) -> Integer", typecheck: :now
|
24
|
+
def calls_bound2(x)
|
25
|
+
uses_bound(x)
|
26
|
+
end
|
27
|
+
|
28
|
+
type "(Integer) -> Integer", typecheck: :fail1
|
29
|
+
def calls_bound3(x)
|
30
|
+
uses_bound(x)
|
31
|
+
end
|
32
|
+
|
33
|
+
type "(1) -> String", typecheck: :fail2
|
34
|
+
def calls_bound4(x)
|
35
|
+
uses_bound(x)
|
36
|
+
end
|
37
|
+
|
38
|
+
type "(1, 1) -> Integer", typecheck: :now
|
39
|
+
def calls_bound5(x, y)
|
40
|
+
uses_bound_twice(x, y)
|
41
|
+
end
|
42
|
+
|
43
|
+
type "(1, 2) -> String", typecheck: :now
|
44
|
+
def calls_bound6(x, y)
|
45
|
+
uses_bound_twice(x, y)
|
46
|
+
end
|
47
|
+
|
48
|
+
type "(1, 2) -> Integer", typecheck: :fail3
|
49
|
+
def calls_bound7(x, y)
|
50
|
+
uses_bound_twice(x, y)
|
51
|
+
end
|
52
|
+
|
53
|
+
type "(1) -> Integer", typecheck: :now
|
54
|
+
def calls_bound8(x)
|
55
|
+
uses_optional(x)
|
56
|
+
end
|
57
|
+
|
58
|
+
type "(1) -> Integer", typecheck: :now
|
59
|
+
def calls_bound9(x)
|
60
|
+
uses_optional('x', x)
|
61
|
+
end
|
62
|
+
|
63
|
+
type "(1) -> String", typecheck: :fail4
|
64
|
+
def calls_bound10(x)
|
65
|
+
uses_optional('x', x)
|
66
|
+
end
|
67
|
+
|
68
|
+
type "(1) -> String", typecheck: :fail5
|
69
|
+
def calls_bound11(x)
|
70
|
+
uses_optional(x)
|
71
|
+
end
|
72
|
+
}
|
73
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :fail1 }
|
74
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :fail2 }
|
75
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :fail3 }
|
76
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :fail4 }
|
77
|
+
assert_raises(RDL::Typecheck::StaticTypeError) { RDL.do_typecheck :fail5 }
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
data/test/test_contract.rb
CHANGED
data/test/test_dsl.rb
CHANGED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
$LOAD_PATH << File.dirname(__FILE__) + "/../lib"
|
3
|
+
require 'rdl'
|
4
|
+
require 'types/core'
|
5
|
+
|
6
|
+
class TestDynChecks < Minitest::Test
|
7
|
+
extend RDL::Annotate
|
8
|
+
|
9
|
+
RDL::Config.instance.check_comp_types = true
|
10
|
+
RDL::Config.instance.rerun_comp_types = true
|
11
|
+
|
12
|
+
type :bar1, "(Integer) -> ``RDL::Type::SingletonType.new(2)``", wrap: false
|
13
|
+
|
14
|
+
type "(Integer) -> Integer", typecheck: :now, wrap: false
|
15
|
+
def foo1(x)
|
16
|
+
bar1(x)
|
17
|
+
end
|
18
|
+
|
19
|
+
## First, a silly example. `bar1` (defined below) always returns 1, but its comp type says it always returns 2.
|
20
|
+
## `foo1` will type check properly, but the `bar1` error won't be caught until `foo` is called after type checking.
|
21
|
+
def test_foo1_fail
|
22
|
+
RDL::Util.silent_warnings { self.class.class_eval("def bar1(x) 1; end") }
|
23
|
+
assert_raises(RDL::Type::TypeError) { self.class.new(nil).foo1(1) }
|
24
|
+
end
|
25
|
+
|
26
|
+
## If we redefine `bar1`, it should work.
|
27
|
+
|
28
|
+
def test_foo1_pass
|
29
|
+
RDL::Util.silent_warnings { self.class.class_eval("def bar1(x) 2; end") }
|
30
|
+
assert self.class.new(nil).foo1(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
## Let's try again, with arrays.
|
35
|
+
|
36
|
+
type :return_array, "() -> ``RDL::Type::TupleType.new(RDL::Type::SingletonType.new(0), RDL::Type::SingletonType.new(0), RDL::Type::SingletonType.new(0))``", wrap: false
|
37
|
+
|
38
|
+
type "() -> Integer", typecheck: :now ## this type check should pass
|
39
|
+
def calls_array
|
40
|
+
a = return_array
|
41
|
+
a[1]
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_array_fail
|
45
|
+
RDL::Util.silent_warnings{ self.class.class_eval("def return_array() [1,2,3]; end") }
|
46
|
+
assert_raises(RDL::Type::TypeError) { self.class.new(nil).calls_array }
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_array_pass
|
50
|
+
RDL::Util.silent_warnings { self.class.class_eval("def return_array() [0,0,0]; end") }
|
51
|
+
assert self.class.new(nil).calls_array
|
52
|
+
end
|
53
|
+
|
54
|
+
# Now for a slightly-but-not-really more realistic example, with a mock (very small) DB schema.
|
55
|
+
|
56
|
+
class People
|
57
|
+
extend RDL::Annotate
|
58
|
+
@people_schema = RDL::Globals.parser.scan_str "#T { name: String, age: Integer }"
|
59
|
+
type 'self.where', "(``raise 'Expected schema' unless @people_schema; @people_schema``) -> Integer", wrap: false
|
60
|
+
def self.where(record)
|
61
|
+
1 ## not actually looking anything up, so just return a dummy int
|
62
|
+
end
|
63
|
+
|
64
|
+
type :person_to_look_up, '() -> {name: String, age: 30}', wrap: false
|
65
|
+
|
66
|
+
type "() -> Integer", wrap: false, typecheck: :now ## type checking will succeed the first time
|
67
|
+
def calls_where
|
68
|
+
self.class.where(person_to_look_up)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_where_fail
|
75
|
+
RDL::Util.silent_warnings{ People.class_eval("def person_to_look_up() {name: 'alice', age: '30'}; end") }
|
76
|
+
assert_raises(RDL::Type::TypeError) { People.new.calls_where }
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_where_pass
|
80
|
+
RDL::Util.silent_warnings { People.class_eval("def person_to_look_up() {name: 'alice', age: 30}; end") }
|
81
|
+
assert People.new.calls_where
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# A test where the same method returns different types in different calls:
|
86
|
+
|
87
|
+
def self.called_thrice_output(trec, targs)
|
88
|
+
if targs[0].is_a?(RDL::Type::NominalType)
|
89
|
+
RDL::Globals.types[:integer]
|
90
|
+
elsif targs[0].is_a?(RDL::Type::SingletonType)
|
91
|
+
RDL::Type::SingletonType.new(targs[0].val + 1)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
type :called_thrice, "(Integer) -> ``called_thrice_output(trec, targs)``", wrap: false
|
96
|
+
|
97
|
+
|
98
|
+
type "(Integer) -> Integer", wrap: false, typecheck: :now
|
99
|
+
def multi_caller(x)
|
100
|
+
called_thrice(x) ## should have type Integer
|
101
|
+
called_thrice(1) ## Should have type 2
|
102
|
+
called_thrice(2) ## Should have type 3
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_multi_pass
|
106
|
+
RDL::Util.silent_warnings { self.class.class_eval "def called_thrice(x) x+1; end" } ## this will satisfy all call return types
|
107
|
+
assert self.class.new(nil).multi_caller(0)
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_multi_fail
|
111
|
+
RDL::Util.silent_warnings { self.class.class_eval "def called_thrice(x) if (x==2) then x+2 else x+1 end; end" } ## silly
|
112
|
+
assert_raises(RDL::Type::TypeError) { multi_caller(0) }
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
# Now to test op_asgn.
|
117
|
+
|
118
|
+
type "(Integer) -> Integer", typecheck: :now, wrap: false
|
119
|
+
def op_asgn_test(x)
|
120
|
+
x += 1
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_op_asgn
|
124
|
+
assert self.class.new(nil).op_asgn_test(1)
|
125
|
+
assert_raises(RDL::Type::TypeError) { self.class.new(nil).op_asgn_test(1.5) } ## Because `op_asgn_test` isn't wrapped, this should only raise error once :+ is called
|
126
|
+
end
|
127
|
+
|
128
|
+
type "([1,2,3]) -> Integer", typecheck: :now, wrap: false
|
129
|
+
def op_asgn_arr(arr)
|
130
|
+
arr[1] += 1
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_op_asgn_arr
|
134
|
+
assert self.class.new(nil).op_asgn_arr([1,2,3])
|
135
|
+
assert_raises(RDL::Type::TypeError) { self.class.new(nil).op_asgn_arr([1,42, 3]) } ## same issue as above
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Now, we'll test the re-running of computed types; this has been tested in all the examples above, but here we'll test that it fails correctly.
|
140
|
+
|
141
|
+
class CompFail
|
142
|
+
extend RDL::Annotate
|
143
|
+
#@@compfail = 1
|
144
|
+
=begin
|
145
|
+
def self.get_val()
|
146
|
+
@@compfail
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.set_val(v)
|
150
|
+
@@compfail = v
|
151
|
+
end
|
152
|
+
|
153
|
+
type "(``if (get_val == 1) then RDL::Globals.types[:integer] else RDL::Type::UnionType.new(RDL::Globals.types[:integer], RDL::Globals.types[:string]) end``) -> Integer", wrap: false ## pathological type depending on @@compfail
|
154
|
+
def bar(x)
|
155
|
+
x
|
156
|
+
end
|
157
|
+
|
158
|
+
type "(Integer) -> Integer", typecheck: :now, wrap: false ## will type check fine
|
159
|
+
def foo(x)
|
160
|
+
bar(x)
|
161
|
+
end
|
162
|
+
=end
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_rerun_comp_type
|
166
|
+
CompFail.class_eval {
|
167
|
+
#RDL::Config.instance.check_comp_types = true
|
168
|
+
#RDL::Config.instance.rerun_comp_types = true
|
169
|
+
|
170
|
+
@@compfail = 1
|
171
|
+
def self.get_val()
|
172
|
+
@@compfail
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.set_val(v)
|
176
|
+
@@compfail = v
|
177
|
+
end
|
178
|
+
|
179
|
+
type "(``if (get_val == 1) then RDL::Globals.types[:integer] else RDL::Type::UnionType.new(RDL::Globals.types[:integer], RDL::Globals.types[:string]) end``) -> Integer", wrap: false ## pathological type depending on @@compfail
|
180
|
+
def bar(x)
|
181
|
+
x
|
182
|
+
end
|
183
|
+
|
184
|
+
RDL::Config.instance.check_comp_types = true
|
185
|
+
RDL::Config.instance.rerun_comp_types = true
|
186
|
+
|
187
|
+
type "(Integer) -> Integer", typecheck: :now, wrap: false ## will type check fine
|
188
|
+
def foo2(x)
|
189
|
+
bar(x)
|
190
|
+
end
|
191
|
+
}
|
192
|
+
|
193
|
+
## These are needed out here too due to weird orderings involving RDL.reset.
|
194
|
+
assert CompFail.new.foo2(1) ## will run fine
|
195
|
+
CompFail.set_val(2) ## change heap
|
196
|
+
assert_raises(RDL::Type::TypeError) { CompFail.new.foo2(1) }
|
197
|
+
|
198
|
+
RDL::Config.instance.check_comp_types = false
|
199
|
+
RDL::Config.instance.rerun_comp_types = false
|
200
|
+
end
|
201
|
+
|
202
|
+
RDL::Config.instance.check_comp_types = false
|
203
|
+
RDL::Config.instance.rerun_comp_types = false
|
204
|
+
|
205
|
+
|
206
|
+
end
|