Skip to content

Commit 90eb909

Browse files
authored
Merge pull request #2644 from ruby/type-finterprint
Introduce `type_fingerprint`
2 parents 3c2c906 + 8628b16 commit 90eb909

File tree

10 files changed

+533
-0
lines changed

10 files changed

+533
-0
lines changed

docs/type_fingerprint.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Type Fingerprint of RBS Inline AST
2+
3+
Type fingerprint of RBS Inline AST is an object that can be used to detect if the RBS Inline AST is updated and the type checker should type check the whole codebase again.
4+
5+
1. If the AST update is related to the type information, the fingerprint is changed -- adding new type, including new module, changing method type, etc. The type checker should type check the codebase with updated type information.
6+
2. If the AST updated is not related to the type information, the fingerprint keeps the last value -- changing the method implementation, adding a method call in the top level, adding white spaces and new lines, etc. The type checker can skip updating the type information, and type checking only the implementation of the file is sufficient.
7+
3. Documentation comments are considered type related information for now.
8+
9+
## Type Fingerprint Calculation
10+
11+
The type fingerprint is calculated by converting AST nodes to standardized data structures that represent only the type-relevant information. Each AST class implements a `type_fingerprint` method that returns mainly arrays and strings.
12+
13+
We expect not using the values for something other than change detection. Compare old and new fingerprints, and we can detect the change between the RBS inline AST if the fingerprints are different.
14+
15+
The fingerprint methods are implemented across:
16+
17+
- `AST::Ruby::Annotations::*#type_fingerprint` - Returns `untyped` (arrays, strings, or nil)
18+
- `AST::Ruby::Members::*#type_fingerprint` - Returns `untyped` (typically arrays)
19+
- `AST::Ruby::Declarations::*#type_fingerprint` - Returns `untyped` (typically arrays)
20+
- `InlineParser::Result#type_fingerprint` - Returns `untyped` (array of declaration fingerprints)
21+

lib/rbs/ast/ruby/annotations.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ def map_type_name
3131
type: type.map_type_name { yield _1 }
3232
) #: self
3333
end
34+
35+
def type_fingerprint
36+
[
37+
"annots/node_type_assertion",
38+
type.to_s
39+
]
40+
end
3441
end
3542

3643
class AliasAnnotation < Base
@@ -55,9 +62,21 @@ def map_type_name
5562
end
5663

5764
class ClassAliasAnnotation < AliasAnnotation
65+
def type_fingerprint
66+
[
67+
"annots/class-alias",
68+
type_name&.to_s
69+
]
70+
end
5871
end
5972

6073
class ModuleAliasAnnotation < AliasAnnotation
74+
def type_fingerprint
75+
[
76+
"annots/module-alias",
77+
type_name&.to_s
78+
]
79+
end
6180
end
6281

6382
class ColonMethodTypeAnnotation < Base
@@ -77,6 +96,14 @@ def map_type_name
7796
method_type: method_type.map_type {|type| type.map_type_name { yield _1 }}
7897
) #: self
7998
end
99+
100+
def type_fingerprint
101+
[
102+
"annots/colon_method_type",
103+
annotations.map(&:to_s),
104+
method_type.to_s
105+
]
106+
end
80107
end
81108

82109
class MethodTypesAnnotation < Base
@@ -100,6 +127,13 @@ def map_type_name(&block)
100127

101128
self.class.new(location:, prefix_location:, overloads: ovs, vertical_bar_locations:) #: self
102129
end
130+
131+
def type_fingerprint
132+
[
133+
"annots/method_types",
134+
overloads.map { |o| [o.annotations.map(&:to_s), o.method_type.to_s] }
135+
]
136+
end
103137
end
104138

105139
class SkipAnnotation < Base
@@ -110,6 +144,10 @@ def initialize(location:, prefix_location:, skip_location:, comment_location:)
110144
@skip_location = skip_location
111145
@comment_location = comment_location
112146
end
147+
148+
def type_fingerprint
149+
"annots/skip"
150+
end
113151
end
114152

115153
class ReturnTypeAnnotation < Base
@@ -139,6 +177,14 @@ def map_type_name(&block)
139177
comment_location: comment_location
140178
) #: self
141179
end
180+
181+
def type_fingerprint
182+
[
183+
"annots/return_type",
184+
return_type.to_s,
185+
comment_location&.source
186+
]
187+
end
142188
end
143189

144190
class TypeApplicationAnnotation < Base
@@ -162,6 +208,13 @@ def map_type_name(&block)
162208
comma_locations:
163209
) #: self
164210
end
211+
212+
def type_fingerprint
213+
[
214+
"annots/type_application",
215+
type_args.map(&:to_s)
216+
]
217+
end
165218
end
166219

167220
class InstanceVariableAnnotation < Base
@@ -187,6 +240,15 @@ def map_type_name(&block)
187240
comment_location:
188241
) #: self
189242
end
243+
244+
def type_fingerprint
245+
[
246+
"annots/instance_variable",
247+
ivar_name.to_s,
248+
type.to_s,
249+
comment_location&.source
250+
]
251+
end
190252
end
191253
end
192254
end

lib/rbs/ast/ruby/declarations.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ def location
5454

5555
alias name type_name
5656
alias args type_args
57+
58+
def type_fingerprint
59+
[
60+
type_name.to_s,
61+
type_annotation&.type_fingerprint
62+
]
63+
end
5764
end
5865

5966
attr_reader :class_name
@@ -91,6 +98,17 @@ def location
9198
def name_location
9299
rbs_location(node.constant_path.location)
93100
end
101+
102+
def type_fingerprint
103+
result = [] #: Array[untyped]
104+
105+
result << "decls/class"
106+
result << class_name.to_s
107+
result << super_class&.type_fingerprint
108+
result << members.map { _1.type_fingerprint }
109+
110+
result
111+
end
94112
end
95113

96114
class ModuleDecl < Base
@@ -128,6 +146,16 @@ def location
128146
def name_location
129147
rbs_location(node.constant_path.location)
130148
end
149+
150+
def type_fingerprint
151+
result = [] #: Array[untyped]
152+
153+
result << "decls/module"
154+
result << module_name.to_s
155+
result << members.map { _1.type_fingerprint}
156+
157+
result
158+
end
131159
end
132160

133161
class ConstantDecl < Base
@@ -181,6 +209,15 @@ def type
181209
def comment
182210
leading_comment&.as_comment
183211
end
212+
213+
def type_fingerprint
214+
[
215+
"decls/constant",
216+
constant_name.to_s,
217+
type.to_s,
218+
leading_comment&.as_comment&.string
219+
]
220+
end
184221
end
185222

186223
class ClassModuleAliasDecl < Base
@@ -227,6 +264,16 @@ def old_name
227264
def comment
228265
leading_comment&.as_comment
229266
end
267+
268+
def type_fingerprint
269+
[
270+
"decls/class_module_alias",
271+
annotation.type_fingerprint,
272+
new_name.to_s,
273+
old_name.to_s,
274+
leading_comment&.as_comment&.string
275+
]
276+
end
230277
end
231278
end
232279
end

lib/rbs/ast/ruby/members.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def map_type_name(&block)
2828
end #: self
2929
end
3030

31+
def type_fingerprint
32+
return_type_annotation&.type_fingerprint
33+
end
34+
3135
def method_type
3236
return_type =
3337
case return_type_annotation
@@ -175,6 +179,17 @@ def overloads
175179
]
176180
end
177181
end
182+
183+
def type_fingerprint
184+
case type_annotations
185+
when DocStyle
186+
type_annotations.type_fingerprint
187+
when Array
188+
type_annotations.map(&:type_fingerprint)
189+
when nil
190+
nil
191+
end
192+
end
178193
end
179194

180195
class DefMember < Base
@@ -212,6 +227,15 @@ def annotations
212227
def name_location
213228
rbs_location(node.name_loc)
214229
end
230+
231+
def type_fingerprint
232+
[
233+
"members/def",
234+
name.to_s,
235+
method_type.type_fingerprint,
236+
leading_comment&.as_comment&.string
237+
]
238+
end
215239
end
216240

217241
class MixinMember < Base
@@ -240,6 +264,15 @@ def name_location
240264
def type_args
241265
annotation&.type_args || []
242266
end
267+
268+
def type_fingerprint
269+
[
270+
"members/mixin",
271+
self.class.name,
272+
module_name.to_s,
273+
annotation&.type_fingerprint
274+
]
275+
end
243276
end
244277

245278
class IncludeMember < MixinMember
@@ -284,6 +317,16 @@ def name_locations
284317
def type
285318
type_annotation&.type
286319
end
320+
321+
def type_fingerprint
322+
[
323+
"members/attribute",
324+
self.class.name,
325+
names.map(&:to_s),
326+
type_annotation&.type_fingerprint,
327+
leading_comment&.as_comment&.string
328+
]
329+
end
287330
end
288331

289332
class AttrReaderMember < AttributeMember
@@ -314,6 +357,13 @@ def type
314357
def location
315358
annotation.location
316359
end
360+
361+
def type_fingerprint
362+
[
363+
"members/instance_variable",
364+
annotation.type_fingerprint
365+
]
366+
end
317367
end
318368
end
319369
end

lib/rbs/inline_parser.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ def initialize(buffer, prism)
1111
@declarations = []
1212
@diagnostics = []
1313
end
14+
15+
def type_fingerprint
16+
declarations.map(&:type_fingerprint)
17+
end
1418
end
1519

1620
module Diagnostic

0 commit comments

Comments
 (0)