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