rbs 1.7.1 → 1.8.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 +4 -4
- data/CHANGELOG.md +23 -0
- data/core/array.rbs +3 -3
- data/core/builtin.rbs +4 -0
- data/core/enumerable.rbs +3 -3
- data/docs/syntax.md +23 -20
- data/ext/rbs_extension/parser.c +96 -94
- data/ext/rbs_extension/ruby_objs.c +8 -6
- data/ext/rbs_extension/ruby_objs.h +2 -2
- data/lib/rbs/ast/declarations.rb +6 -2
- data/lib/rbs/cli.rb +1 -1
- data/lib/rbs/definition_builder.rb +29 -2
- data/lib/rbs/environment.rb +1 -0
- data/lib/rbs/environment_walker.rb +4 -1
- data/lib/rbs/errors.rb +12 -0
- data/lib/rbs/type_alias_regularity.rb +115 -0
- data/lib/rbs/types.rb +10 -22
- data/lib/rbs/validator.rb +40 -7
- data/lib/rbs/variance_calculator.rb +52 -24
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +1 -1
- data/lib/rbs.rb +1 -0
- data/schema/decls.json +13 -1
- data/schema/types.json +8 -2
- data/sig/declarations.rbs +9 -6
- data/sig/definition_builder.rbs +29 -0
- data/sig/environment_walker.rbs +26 -0
- data/sig/errors.rbs +10 -0
- data/sig/type_alias_regularity.rbs +92 -0
- data/sig/types.rbs +11 -8
- data/sig/validator.rbs +7 -0
- data/sig/variance_calculator.rbs +50 -0
- data/stdlib/date/0/date.rbs +2 -2
- data/stdlib/set/0/set.rbs +3 -3
- data/steep/Gemfile.lock +10 -10
- metadata +4 -2
@@ -0,0 +1,115 @@
|
|
1
|
+
module RBS
|
2
|
+
class TypeAliasRegularity
|
3
|
+
class Diagnostic
|
4
|
+
attr_reader :type_name, :nonregular_type
|
5
|
+
|
6
|
+
def initialize(type_name:, nonregular_type:)
|
7
|
+
@type_name = type_name
|
8
|
+
@nonregular_type = nonregular_type
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :env, :builder, :diagnostics
|
13
|
+
|
14
|
+
def initialize(env:)
|
15
|
+
@env = env
|
16
|
+
@builder = DefinitionBuilder.new(env: env)
|
17
|
+
@diagnostics = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate
|
21
|
+
diagnostics.clear
|
22
|
+
|
23
|
+
each_mutual_alias_defs do |names|
|
24
|
+
# Find the first generic type alias in strongly connected component.
|
25
|
+
# This is to skip the regularity check when the alias is not generic.
|
26
|
+
names.each do |name|
|
27
|
+
# @type break: nil
|
28
|
+
if type = build_alias_type(name)
|
29
|
+
# Running validation only once from the first generic type is enough, because they are mutual recursive definition.
|
30
|
+
validate_alias_type(type, names, {})
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_alias_type(alias_type, names, types)
|
38
|
+
if names.include?(alias_type.name)
|
39
|
+
if ex_type = types[alias_type.name]
|
40
|
+
unless compatible_args?(ex_type.args, alias_type.args)
|
41
|
+
diagnostics[alias_type.name] ||=
|
42
|
+
Diagnostic.new(type_name: alias_type.name, nonregular_type: alias_type)
|
43
|
+
end
|
44
|
+
|
45
|
+
return
|
46
|
+
else
|
47
|
+
types[alias_type.name] = alias_type
|
48
|
+
end
|
49
|
+
|
50
|
+
expanded = builder.expand_alias2(alias_type.name, alias_type.args)
|
51
|
+
each_alias_type(expanded) do |at|
|
52
|
+
validate_alias_type(at, names, types)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_alias_type(name)
|
58
|
+
entry = env.alias_decls[name] or raise "Unknown alias name: #{name}"
|
59
|
+
unless entry.decl.type_params.empty?
|
60
|
+
as = entry.decl.type_params.each.map {|param| Types::Variable.new(name: param.name, location: nil) }
|
61
|
+
Types::Alias.new(name: name, args: as, location: nil)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def compatible_args?(args1, args2)
|
66
|
+
if args1.size == args2.size
|
67
|
+
args1.zip(args2).all? do |t1, t2|
|
68
|
+
t1.is_a?(Types::Bases::Any) ||
|
69
|
+
t2.is_a?(Types::Bases::Any) ||
|
70
|
+
t1 == t2
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def nonregular?(type_name)
|
76
|
+
diagnostics[type_name]
|
77
|
+
end
|
78
|
+
|
79
|
+
def each_mutual_alias_defs(&block)
|
80
|
+
# @type var each_node: TSort::_EachNode[TypeName]
|
81
|
+
each_node = __skip__ = -> (&block) do
|
82
|
+
env.alias_decls.each_value do |decl|
|
83
|
+
block[decl.name]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# @type var each_child: TSort::_EachChild[TypeName]
|
87
|
+
each_child = __skip__ = -> (name, &block) do
|
88
|
+
type = builder.expand_alias1(name)
|
89
|
+
each_alias_type(type) do |ty|
|
90
|
+
block[ty.name]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
TSort.each_strongly_connected_component(each_node, each_child) do |names|
|
95
|
+
yield Set.new(names)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def each_alias_type(type, &block)
|
100
|
+
if type.is_a?(RBS::Types::Alias)
|
101
|
+
yield type
|
102
|
+
end
|
103
|
+
|
104
|
+
type.each_type do |ty|
|
105
|
+
each_alias_type(ty, &block)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.validate(env:)
|
110
|
+
self.new(env: env).tap do |validator|
|
111
|
+
validator.validate()
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/rbs/types.rb
CHANGED
@@ -295,39 +295,27 @@ module RBS
|
|
295
295
|
|
296
296
|
class Alias
|
297
297
|
attr_reader :location
|
298
|
-
attr_reader :name
|
299
298
|
|
300
|
-
|
299
|
+
include Application
|
300
|
+
|
301
|
+
def initialize(name:, args:, location:)
|
301
302
|
@name = name
|
303
|
+
@args = args
|
302
304
|
@location = location
|
303
305
|
end
|
304
306
|
|
305
|
-
def ==(other)
|
306
|
-
other.is_a?(Alias) && other.name == name
|
307
|
-
end
|
308
|
-
|
309
|
-
alias eql? ==
|
310
|
-
|
311
|
-
def hash
|
312
|
-
self.class.hash ^ name.hash
|
313
|
-
end
|
314
|
-
|
315
|
-
include NoFreeVariables
|
316
|
-
include NoSubst
|
317
|
-
|
318
307
|
def to_json(state = _ = nil)
|
319
|
-
{ class: :alias, name: name, location: location }.to_json(state)
|
308
|
+
{ class: :alias, name: name, args: args, location: location }.to_json(state)
|
320
309
|
end
|
321
310
|
|
322
|
-
def
|
323
|
-
name.
|
311
|
+
def sub(s)
|
312
|
+
Alias.new(name: name, args: args.map {|ty| ty.sub(s) }, location: location)
|
324
313
|
end
|
325
314
|
|
326
|
-
|
327
|
-
|
328
|
-
def map_type_name
|
315
|
+
def map_type_name(&block)
|
329
316
|
Alias.new(
|
330
317
|
name: yield(name, location, self),
|
318
|
+
args: args.map {|arg| arg.map_type_name(&block) },
|
331
319
|
location: location
|
332
320
|
)
|
333
321
|
end
|
@@ -433,7 +421,7 @@ module RBS
|
|
433
421
|
return "{ }" if self.fields.empty?
|
434
422
|
|
435
423
|
fields = self.fields.map do |key, type|
|
436
|
-
if key.is_a?(Symbol) && key.match?(/\A[A-Za-z_][A-Za-z_]*\z/)
|
424
|
+
if key.is_a?(Symbol) && key.match?(/\A[A-Za-z_][A-Za-z_]*\z/)
|
437
425
|
"#{key}: #{type}"
|
438
426
|
else
|
439
427
|
"#{key.inspect} => #{type}"
|
data/lib/rbs/validator.rb
CHANGED
@@ -2,10 +2,12 @@ module RBS
|
|
2
2
|
class Validator
|
3
3
|
attr_reader :env
|
4
4
|
attr_reader :resolver
|
5
|
+
attr_reader :definition_builder
|
5
6
|
|
6
7
|
def initialize(env:, resolver:)
|
7
8
|
@env = env
|
8
9
|
@resolver = resolver
|
10
|
+
@definition_builder = DefinitionBuilder.new(env: env)
|
9
11
|
end
|
10
12
|
|
11
13
|
def absolute_type(type, context:)
|
@@ -17,8 +19,8 @@ module RBS
|
|
17
19
|
# Validates presence of the relative type, and application arity match.
|
18
20
|
def validate_type(type, context:)
|
19
21
|
case type
|
20
|
-
when Types::ClassInstance, Types::Interface
|
21
|
-
# @type var type: Types::ClassInstance | Types::Interface
|
22
|
+
when Types::ClassInstance, Types::Interface, Types::Alias
|
23
|
+
# @type var type: Types::ClassInstance | Types::Interface | Types::Alias
|
22
24
|
if type.name.namespace.relative?
|
23
25
|
type = _ = absolute_type(type, context: context) do |_|
|
24
26
|
NoTypeFoundError.check!(type.name.absolute!, env: env, location: type.location)
|
@@ -30,6 +32,8 @@ module RBS
|
|
30
32
|
env.class_decls[type.name]&.type_params
|
31
33
|
when Types::Interface
|
32
34
|
env.interface_decls[type.name]&.decl&.type_params
|
35
|
+
when Types::Alias
|
36
|
+
env.alias_decls[type.name]&.decl&.type_params
|
33
37
|
end
|
34
38
|
|
35
39
|
unless type_params
|
@@ -43,8 +47,8 @@ module RBS
|
|
43
47
|
location: type.location
|
44
48
|
)
|
45
49
|
|
46
|
-
when Types::
|
47
|
-
# @type var type: Types::
|
50
|
+
when Types::ClassSingleton
|
51
|
+
# @type var type: Types::ClassSingleton
|
48
52
|
type = _ = absolute_type(type, context: context) { type.name.absolute! }
|
49
53
|
NoTypeFoundError.check!(type.name, env: env, location: type.location)
|
50
54
|
end
|
@@ -55,11 +59,40 @@ module RBS
|
|
55
59
|
end
|
56
60
|
|
57
61
|
def validate_type_alias(entry:)
|
58
|
-
|
59
|
-
|
62
|
+
type_name = entry.decl.name
|
63
|
+
|
64
|
+
if type_alias_dependency.circular_definition?(type_name)
|
60
65
|
location = entry.decl.location or raise
|
61
|
-
raise RecursiveTypeAliasError.new(alias_names: [
|
66
|
+
raise RecursiveTypeAliasError.new(alias_names: [type_name], location: location)
|
67
|
+
end
|
68
|
+
|
69
|
+
if diagnostic = type_alias_regularity.nonregular?(type_name)
|
70
|
+
location = entry.decl.location or raise
|
71
|
+
raise NonregularTypeAliasError.new(diagnostic: diagnostic, location: location)
|
72
|
+
end
|
73
|
+
|
74
|
+
unless entry.decl.type_params.empty?
|
75
|
+
calculator = VarianceCalculator.new(builder: definition_builder)
|
76
|
+
result = calculator.in_type_alias(name: type_name)
|
77
|
+
if set = result.incompatible?(entry.decl.type_params)
|
78
|
+
set.each do |param_name|
|
79
|
+
param = entry.decl.type_params[param_name] or raise
|
80
|
+
raise InvalidVarianceAnnotationError.new(
|
81
|
+
type_name: type_name,
|
82
|
+
param: param,
|
83
|
+
location: entry.decl.type.location
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
62
87
|
end
|
63
88
|
end
|
89
|
+
|
90
|
+
def type_alias_dependency
|
91
|
+
@type_alias_dependency ||= TypeAliasDependency.new(env: env)
|
92
|
+
end
|
93
|
+
|
94
|
+
def type_alias_regularity
|
95
|
+
@type_alias_regularity ||= TypeAliasRegularity.validate(env: env)
|
96
|
+
end
|
64
97
|
end
|
65
98
|
end
|
@@ -54,6 +54,21 @@ module RBS
|
|
54
54
|
false
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
def incompatible?(params)
|
59
|
+
# @type set: Hash[Symbol]
|
60
|
+
set = Set[]
|
61
|
+
|
62
|
+
params.each do |param|
|
63
|
+
unless compatible?(param.name, with_annotation: param.variance)
|
64
|
+
set << param.name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
unless set.empty?
|
69
|
+
set
|
70
|
+
end
|
71
|
+
end
|
57
72
|
end
|
58
73
|
|
59
74
|
attr_reader :builder
|
@@ -69,19 +84,12 @@ module RBS
|
|
69
84
|
def in_method_type(method_type:, variables:)
|
70
85
|
result = Result.new(variables: variables)
|
71
86
|
|
72
|
-
method_type.type
|
73
|
-
type(param.type, result: result, context: :contravariant)
|
74
|
-
end
|
87
|
+
function(method_type.type, result: result, context: :covariant)
|
75
88
|
|
76
89
|
if block = method_type.block
|
77
|
-
block.type
|
78
|
-
type(param.type, result: result, context: :covariant)
|
79
|
-
end
|
80
|
-
type(block.type.return_type, result: result, context: :contravariant)
|
90
|
+
function(block.type, result: result, context: :contravariant)
|
81
91
|
end
|
82
92
|
|
83
|
-
type(method_type.type.return_type, result: result, context: :covariant)
|
84
|
-
|
85
93
|
result
|
86
94
|
end
|
87
95
|
|
@@ -97,6 +105,14 @@ module RBS
|
|
97
105
|
end
|
98
106
|
end
|
99
107
|
|
108
|
+
def in_type_alias(name:)
|
109
|
+
decl = env.alias_decls[name].decl or raise
|
110
|
+
variables = decl.type_params.each.map(&:name)
|
111
|
+
Result.new(variables: variables).tap do |result|
|
112
|
+
type(decl.type, result: result, context: :covariant)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
100
116
|
def type(type, result:, context:)
|
101
117
|
case type
|
102
118
|
when Types::Variable
|
@@ -110,7 +126,7 @@ module RBS
|
|
110
126
|
result.invariant(type.name)
|
111
127
|
end
|
112
128
|
end
|
113
|
-
when Types::ClassInstance, Types::Interface
|
129
|
+
when Types::ClassInstance, Types::Interface, Types::Alias
|
114
130
|
NoTypeFoundError.check!(type.name,
|
115
131
|
env: env,
|
116
132
|
location: type.location)
|
@@ -120,6 +136,8 @@ module RBS
|
|
120
136
|
env.class_decls[type.name].type_params
|
121
137
|
when Types::Interface
|
122
138
|
env.interface_decls[type.name].decl.type_params
|
139
|
+
when Types::Alias
|
140
|
+
env.alias_decls[type.name].decl.type_params
|
123
141
|
end
|
124
142
|
|
125
143
|
type.args.each.with_index do |ty, i|
|
@@ -130,26 +148,36 @@ module RBS
|
|
130
148
|
when :covariant
|
131
149
|
type(ty, result: result, context: context)
|
132
150
|
when :contravariant
|
133
|
-
|
134
|
-
con = case context
|
135
|
-
when :invariant
|
136
|
-
:invariant
|
137
|
-
when :covariant
|
138
|
-
:contravariant
|
139
|
-
when :contravariant
|
140
|
-
:covariant
|
141
|
-
else
|
142
|
-
raise
|
143
|
-
end
|
144
|
-
type(ty, result: result, context: con)
|
151
|
+
type(ty, result: result, context: negate(context))
|
145
152
|
end
|
146
153
|
end
|
147
|
-
when Types::
|
148
|
-
|
154
|
+
when Types::Proc
|
155
|
+
function(type.type, result: result, context: context)
|
156
|
+
else
|
149
157
|
type.each_type do |ty|
|
150
158
|
type(ty, result: result, context: context)
|
151
159
|
end
|
152
160
|
end
|
153
161
|
end
|
162
|
+
|
163
|
+
def function(type, result:, context:)
|
164
|
+
type.each_param do |param|
|
165
|
+
type(param.type, result: result, context: negate(context))
|
166
|
+
end
|
167
|
+
type(type.return_type, result: result, context: context)
|
168
|
+
end
|
169
|
+
|
170
|
+
def negate(variance)
|
171
|
+
case variance
|
172
|
+
when :invariant
|
173
|
+
:invariant
|
174
|
+
when :covariant
|
175
|
+
:contravariant
|
176
|
+
when :contravariant
|
177
|
+
:covariant
|
178
|
+
else
|
179
|
+
raise
|
180
|
+
end
|
181
|
+
end
|
154
182
|
end
|
155
183
|
end
|
data/lib/rbs/version.rb
CHANGED
data/lib/rbs/writer.rb
CHANGED
@@ -119,7 +119,7 @@ module RBS
|
|
119
119
|
when AST::Declarations::Alias
|
120
120
|
write_comment decl.comment
|
121
121
|
write_annotation decl.annotations
|
122
|
-
puts "type #{decl.name} = #{decl.type}"
|
122
|
+
puts "type #{name_and_params(decl.name, decl.type_params)} = #{decl.type}"
|
123
123
|
|
124
124
|
when AST::Declarations::Interface
|
125
125
|
write_comment decl.comment
|
data/lib/rbs.rb
CHANGED
data/schema/decls.json
CHANGED
@@ -12,6 +12,18 @@
|
|
12
12
|
"name": {
|
13
13
|
"type": "string"
|
14
14
|
},
|
15
|
+
"type_params": {
|
16
|
+
"type": "object",
|
17
|
+
"properties": {
|
18
|
+
"params": {
|
19
|
+
"type": "array",
|
20
|
+
"items": {
|
21
|
+
"$ref": "#/definitions/moduleTypeParam"
|
22
|
+
}
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"required": ["params"]
|
26
|
+
},
|
15
27
|
"type": {
|
16
28
|
"$ref": "types.json"
|
17
29
|
},
|
@@ -28,7 +40,7 @@
|
|
28
40
|
"$ref": "comment.json"
|
29
41
|
}
|
30
42
|
},
|
31
|
-
"required": ["declaration", "name", "type", "annotations", "location", "comment"]
|
43
|
+
"required": ["declaration", "name", "type_params", "type", "annotations", "location", "comment"]
|
32
44
|
},
|
33
45
|
"constant": {
|
34
46
|
"title": "Constant declaration: `VERSION: String`, ...",
|
data/schema/types.json
CHANGED
@@ -106,7 +106,7 @@
|
|
106
106
|
"required": ["class", "name", "args", "location"]
|
107
107
|
},
|
108
108
|
"alias": {
|
109
|
-
"title": "Type alias: `u`, `ty`, `json`, ...",
|
109
|
+
"title": "Type alias: `u`, `ty`, `json`, `list[Integer]`, ...",
|
110
110
|
"type": "object",
|
111
111
|
"properties": {
|
112
112
|
"class": {
|
@@ -116,11 +116,17 @@
|
|
116
116
|
"name": {
|
117
117
|
"type": "string"
|
118
118
|
},
|
119
|
+
"args": {
|
120
|
+
"type": "array",
|
121
|
+
"items": {
|
122
|
+
"$ref": "#"
|
123
|
+
}
|
124
|
+
},
|
119
125
|
"location": {
|
120
126
|
"$ref": "location.json"
|
121
127
|
}
|
122
128
|
},
|
123
|
-
"required": ["class", "name", "location"]
|
129
|
+
"required": ["class", "name", "args", "location"]
|
124
130
|
},
|
125
131
|
"tuple": {
|
126
132
|
"title": "Tuple type: `[Foo, bar]`, ...",
|
data/sig/declarations.rbs
CHANGED
@@ -217,19 +217,22 @@ module RBS
|
|
217
217
|
end
|
218
218
|
|
219
219
|
class Alias < Base
|
220
|
-
# type loc = Location
|
221
|
-
# ^^^^
|
222
|
-
# ^^^
|
223
|
-
#
|
224
|
-
|
220
|
+
# type loc[T] = Location[T, bot]
|
221
|
+
# ^^^^ keyword
|
222
|
+
# ^^^ name
|
223
|
+
# ^^^ type_params
|
224
|
+
# ^ eq
|
225
|
+
#
|
226
|
+
type loc = Location[:keyword | :name | :eq, :type_params]
|
225
227
|
|
226
228
|
attr_reader name: TypeName
|
229
|
+
attr_reader type_params: ModuleTypeParams
|
227
230
|
attr_reader type: Types::t
|
228
231
|
attr_reader annotations: Array[Annotation]
|
229
232
|
attr_reader location: loc?
|
230
233
|
attr_reader comment: Comment?
|
231
234
|
|
232
|
-
def initialize: (name: TypeName, type: Types::t, annotations: Array[Annotation], location: loc?, comment: Comment?) -> void
|
235
|
+
def initialize: (name: TypeName, type_params: ModuleTypeParams, type: Types::t, annotations: Array[Annotation], location: loc?, comment: Comment?) -> void
|
233
236
|
|
234
237
|
include _HashEqual
|
235
238
|
include _ToJson
|
data/sig/definition_builder.rbs
CHANGED
@@ -43,8 +43,37 @@ module RBS
|
|
43
43
|
|
44
44
|
def define_methods: (Definition, interface_methods: Hash[Symbol, Definition::Method], methods: MethodBuilder::Methods, super_interface_method: bool) -> void
|
45
45
|
|
46
|
+
# Expand a type alias of given name without type arguments.
|
47
|
+
# Raises an error if the type alias requires arguments.
|
48
|
+
#
|
49
|
+
# Assume `type foo[T] = [T, T]`:
|
50
|
+
#
|
51
|
+
# ```
|
52
|
+
# expand_alias("::foo") # => error
|
53
|
+
# ```
|
54
|
+
#
|
46
55
|
def expand_alias: (TypeName) -> Types::t
|
47
56
|
|
57
|
+
# Expand a type alias of given name with arguments of `untyped`.
|
58
|
+
#
|
59
|
+
# Assume `type foo[T] = [T, T]`:
|
60
|
+
#
|
61
|
+
# ```
|
62
|
+
# expand_alias1("::foo") # => [untyped, untyped]
|
63
|
+
# ```
|
64
|
+
#
|
65
|
+
def expand_alias1: (TypeName) -> Types::t
|
66
|
+
|
67
|
+
# Expand a type alias of given name with `args`.
|
68
|
+
#
|
69
|
+
# Assume `type foo[T] = [T, T]`:
|
70
|
+
#
|
71
|
+
# ```
|
72
|
+
# expand_alias2("::foo", ["::Integer"]) # => [::Integer, ::Integer]
|
73
|
+
# ```
|
74
|
+
#
|
75
|
+
def expand_alias2: (TypeName, Array[Types::t] args) -> Types::t
|
76
|
+
|
48
77
|
def update: (env: Environment, ancestor_builder: AncestorBuilder, except: _Each[TypeName]) -> DefinitionBuilder
|
49
78
|
end
|
50
79
|
end
|
data/sig/environment_walker.rbs
CHANGED
@@ -1,4 +1,28 @@
|
|
1
1
|
module RBS
|
2
|
+
# EnvironmentWalker provides topological sort of class/module definitions.
|
3
|
+
#
|
4
|
+
# If a method, attribute, or ancestor in a class definition have a reference to another class, it is dependency.
|
5
|
+
#
|
6
|
+
# ```rb
|
7
|
+
# walker = EnvironmentWalker.new(env: env)
|
8
|
+
#
|
9
|
+
# walker.each_strongly_connected_component do |scc|
|
10
|
+
# # Yields an array of strongly connected components.
|
11
|
+
# end
|
12
|
+
# ```
|
13
|
+
#
|
14
|
+
# The `#only_ancestors!` method limits the dependency only to ancestors.
|
15
|
+
# Only super classes and included modules are dependencies with the option.
|
16
|
+
# This is useful to calculate the dependencies of class hierarchy.
|
17
|
+
#
|
18
|
+
# ```rb
|
19
|
+
# walker = EnvironmentWalker.new(env: env).only_ancestors!
|
20
|
+
#
|
21
|
+
# walker.each_strongly_connected_component do |scc|
|
22
|
+
# # Yields an array of strongly connected components.
|
23
|
+
# end
|
24
|
+
# ```
|
25
|
+
#
|
2
26
|
class EnvironmentWalker
|
3
27
|
class InstanceNode
|
4
28
|
attr_reader type_name: TypeName
|
@@ -32,6 +56,8 @@ module RBS
|
|
32
56
|
|
33
57
|
def tsort_each_child: (node) { (node) -> void } -> void
|
34
58
|
|
59
|
+
private
|
60
|
+
|
35
61
|
def each_type_name: (Types::t) { (TypeName) -> void } -> void
|
36
62
|
|
37
63
|
def each_type_node: (Types::t) { (node) -> void } -> void
|
data/sig/errors.rbs
CHANGED
@@ -220,4 +220,14 @@ module RBS
|
|
220
220
|
|
221
221
|
def name: () -> String
|
222
222
|
end
|
223
|
+
|
224
|
+
class NonregularTypeAliasError < LoadingError
|
225
|
+
# Diagnostic reported from `TypeAliasRegularity`.
|
226
|
+
attr_reader diagnostic: TypeAliasRegularity::Diagnostic
|
227
|
+
|
228
|
+
# Location of the definition.
|
229
|
+
attr_reader location: Location[untyped, untyped]?
|
230
|
+
|
231
|
+
def initialize: (diagnostic: TypeAliasRegularity::Diagnostic, location: Location[untyped, untyped]?) -> void
|
232
|
+
end
|
223
233
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module RBS
|
2
|
+
# `TypeAliasRegularity` validates if a type alias is regular or not.
|
3
|
+
#
|
4
|
+
# Generic and recursive type alias cannot be polymorphic in their definitions.
|
5
|
+
#
|
6
|
+
# ```rbs
|
7
|
+
# type foo[T] = Integer
|
8
|
+
# | foo[T]? # Allowed. The type argument of `foo` doesn't change.
|
9
|
+
#
|
10
|
+
# type bar[T] = Integer
|
11
|
+
# | foo[T]
|
12
|
+
# | foo[Array[T]] # Allowed. There are two type arguments `T` and `Array[T]` of `foo`, but it's not definition of `foo`.
|
13
|
+
#
|
14
|
+
# type baz[T] = Integer
|
15
|
+
# | baz[Array[T]] # Error. Recursive definition of `baz` has different type argument from the definition.
|
16
|
+
# ```
|
17
|
+
#
|
18
|
+
# The `#nonregular?` method can be used to test if given type name is regular or not.
|
19
|
+
#
|
20
|
+
# ```rb
|
21
|
+
# validator = RBS::TypeAliasRegularity.validate(env: env)
|
22
|
+
#
|
23
|
+
# validator.nonregular?(TypeName("::foo")) # => nil
|
24
|
+
# validator.nonregular?(TypeName("::bar")) # => nil
|
25
|
+
# validator.nonregular?(TypeName("::baz")) # => TypeAliasRegularity::Diagnostic
|
26
|
+
# ```
|
27
|
+
#
|
28
|
+
# A special case is when the type argument is `untyped`.
|
29
|
+
#
|
30
|
+
# ```rbs
|
31
|
+
# type foo[T] = Integer | foo[untyped] # This is allowed.
|
32
|
+
# ```
|
33
|
+
#
|
34
|
+
class TypeAliasRegularity
|
35
|
+
attr_reader env: Environment
|
36
|
+
|
37
|
+
attr_reader builder: DefinitionBuilder
|
38
|
+
|
39
|
+
attr_reader diagnostics: Hash[TypeName, Diagnostic]
|
40
|
+
|
41
|
+
# `Diagnostic` represents an non-regular type alias declaration error.
|
42
|
+
# It consists of the name of the alias type and a type on which the nonregularity is detected.
|
43
|
+
#
|
44
|
+
# ```rbs
|
45
|
+
# type t[T] = Integer | t[T?]
|
46
|
+
# ```
|
47
|
+
#
|
48
|
+
# The type `t` is nonregular because it contains `t[T?]` on it's right hand side.
|
49
|
+
#
|
50
|
+
# ```
|
51
|
+
# diagnostic = validator.nonregular?(TypeName("::t"))
|
52
|
+
# diagnostic.type_name # => TypeName("::t")
|
53
|
+
# diagnostic.nonregular_type # => t[T?]
|
54
|
+
# ```
|
55
|
+
#
|
56
|
+
class Diagnostic
|
57
|
+
attr_reader type_name: TypeName
|
58
|
+
|
59
|
+
attr_reader nonregular_type: Types::Alias
|
60
|
+
|
61
|
+
def initialize: (type_name: TypeName, nonregular_type: Types::Alias) -> void
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns new instance which already run `#validate`.
|
65
|
+
#
|
66
|
+
def self.validate: (env: Environment) -> TypeAliasRegularity
|
67
|
+
|
68
|
+
def initialize: (env: Environment) -> void
|
69
|
+
|
70
|
+
# Returns `Diagnostic` instance if the alias type is nonregular.
|
71
|
+
# Regurns `nil` if the alias type is regular.
|
72
|
+
#
|
73
|
+
def nonregular?: (TypeName) -> Diagnostic?
|
74
|
+
|
75
|
+
def validate: () -> void
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def validate_alias_type: (Types::Alias, Set[TypeName], Hash[TypeName, Types::Alias]) -> void
|
80
|
+
|
81
|
+
# Returns alias type for given type name, if the alias is generic.
|
82
|
+
# Returns nil if the type alias is not generic.
|
83
|
+
#
|
84
|
+
def build_alias_type: (TypeName) -> Types::Alias?
|
85
|
+
|
86
|
+
def compatible_args?: (Array[Types::t], Array[Types::t]) -> boolish
|
87
|
+
|
88
|
+
def each_alias_type: (Types::t) { (Types::Alias) -> void } -> void
|
89
|
+
|
90
|
+
def each_mutual_alias_defs: () { (Set[TypeName]) -> void } -> void
|
91
|
+
end
|
92
|
+
end
|