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