typeprof 0.30.0 → 0.31.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -4
  3. data/doc/doc.ja.md +1 -1
  4. data/lib/typeprof/cli/cli.rb +28 -10
  5. data/lib/typeprof/cli.rb +1 -0
  6. data/lib/typeprof/code_range.rb +9 -7
  7. data/lib/typeprof/core/ast/base.rb +27 -10
  8. data/lib/typeprof/core/ast/call.rb +90 -21
  9. data/lib/typeprof/core/ast/const.rb +7 -12
  10. data/lib/typeprof/core/ast/control.rb +349 -74
  11. data/lib/typeprof/core/ast/meta.rb +5 -5
  12. data/lib/typeprof/core/ast/method.rb +19 -5
  13. data/lib/typeprof/core/ast/misc.rb +21 -2
  14. data/lib/typeprof/core/ast/module.rb +10 -7
  15. data/lib/typeprof/core/ast/pattern.rb +9 -1
  16. data/lib/typeprof/core/ast/sig_decl.rb +163 -42
  17. data/lib/typeprof/core/ast/sig_type.rb +394 -24
  18. data/lib/typeprof/core/ast/value.rb +11 -3
  19. data/lib/typeprof/core/ast/variable.rb +32 -2
  20. data/lib/typeprof/core/ast.rb +15 -4
  21. data/lib/typeprof/core/builtin.rb +15 -9
  22. data/lib/typeprof/core/env/method.rb +21 -16
  23. data/lib/typeprof/core/env/method_entity.rb +11 -2
  24. data/lib/typeprof/core/env/module_entity.rb +57 -0
  25. data/lib/typeprof/core/env/narrowing.rb +131 -0
  26. data/lib/typeprof/core/env/static_read.rb +9 -8
  27. data/lib/typeprof/core/env.rb +43 -12
  28. data/lib/typeprof/core/graph/box.rb +218 -101
  29. data/lib/typeprof/core/graph/change_set.rb +44 -37
  30. data/lib/typeprof/core/graph/vertex.rb +7 -21
  31. data/lib/typeprof/core/service.rb +61 -40
  32. data/lib/typeprof/core/type.rb +52 -123
  33. data/lib/typeprof/core.rb +1 -1
  34. data/lib/typeprof/diagnostic.rb +5 -6
  35. data/lib/typeprof/lsp/messages.rb +27 -36
  36. data/lib/typeprof/lsp/server.rb +144 -25
  37. data/lib/typeprof/lsp/text.rb +1 -0
  38. data/lib/typeprof/lsp/util.rb +0 -10
  39. data/lib/typeprof/version.rb +1 -1
  40. data/typeprof.conf.jsonc +22 -0
  41. data/typeprof.gemspec +1 -0
  42. metadata +19 -5
  43. data/lib/typeprof/core/graph.rb +0 -3
@@ -11,6 +11,24 @@ module TypeProf::Core
11
11
  s.start_with?("(") && s.end_with?(")") ? s[1..-2] || raise : s
12
12
  end
13
13
 
14
+ def self.strip_array(s)
15
+ s.start_with?("Array[") && s.end_with?("]") ? s[6..-2] || raise : s
16
+ end
17
+
18
+ def self.extract_hash_value_type(s)
19
+ if s.start_with?("Hash[") && s.end_with?("]")
20
+ type = RBS::Parser.parse_type(s)
21
+
22
+ if type.is_a?(RBS::Types::Union)
23
+ type.types.map {|t| t.args[1].to_s }.join(" | ")
24
+ else
25
+ type.args[1].to_s
26
+ end
27
+ else
28
+ s
29
+ end
30
+ end
31
+
14
32
  def self.default_param_map(genv, ty)
15
33
  ty = ty.base_type(genv)
16
34
  instance_ty = ty.is_a?(Type::Instance) ? ty : Type::Instance.new(genv, ty.mod, []) # TODO: type params
@@ -36,29 +54,6 @@ module TypeProf::Core
36
54
  self
37
55
  end
38
56
 
39
- def check_match(genv, changes, vtx)
40
- vtx.each_type do |other_ty|
41
- case other_ty
42
- when Singleton
43
- other_mod = other_ty.mod
44
- if other_mod.module?
45
- # TODO: implement
46
- else
47
- mod = @mod
48
- while mod
49
- return true if mod == other_mod
50
- changes.add_depended_superclass(mod)
51
- mod = mod.superclass
52
- end
53
- end
54
- when Instance
55
- base_ty = @mod.module? ? genv.mod_type : genv.cls_type
56
- return true if base_ty.check_match(genv, changes, Source.new(other_ty))
57
- end
58
- end
59
- return false
60
- end
61
-
62
57
  def show
63
58
  "singleton(#{ @mod.show_cpath })"
64
59
  end
@@ -84,60 +79,6 @@ module TypeProf::Core
84
79
  self
85
80
  end
86
81
 
87
- def check_match(genv, changes, vtx)
88
- vtx.each_type do |other_ty|
89
- case other_ty
90
- when Instance
91
- ty = self
92
- while ty
93
- if ty.mod == other_ty.mod
94
- args_all_match = true
95
- ty.args.zip(other_ty.args) do |arg, other_arg|
96
- unless arg.check_match(genv, changes, other_arg)
97
- args_all_match = false
98
- break
99
- end
100
- end
101
- return true if args_all_match
102
- end
103
- changes.add_depended_superclass(ty.mod)
104
-
105
- if other_ty.mod.module?
106
- return true if check_match_included_modules(genv, changes, ty, other_ty)
107
- end
108
-
109
- ty = genv.get_superclass_type(ty, changes, {})
110
- end
111
- end
112
- end
113
- return false
114
- end
115
-
116
- def check_match_included_modules(genv, changes, ty, other_ty)
117
- ty.mod.included_modules.each do |inc_decl, inc_mod|
118
- if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params
119
- inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, {}, ty)
120
- else
121
- type_params = inc_mod.type_params.map {|ty_param| Source.new() } # TODO: better support
122
- inc_ty = Type::Instance.new(genv, inc_mod, type_params)
123
- end
124
- if inc_ty.mod == other_ty.mod
125
- args_all_match = true
126
- inc_ty.args.zip(other_ty.args) do |arg, other_arg|
127
- if other_arg && !arg.check_match(genv, changes, other_arg)
128
- args_all_match = false
129
- break
130
- end
131
- end
132
- return true if args_all_match
133
- end
134
- changes.add_depended_superclass(inc_ty.mod)
135
-
136
- return true if check_match_included_modules(genv, changes, inc_ty, other_ty)
137
- end
138
- return false
139
- end
140
-
141
82
  def show
142
83
  case @mod.cpath
143
84
  when [:NilClass] then "nil"
@@ -200,24 +141,6 @@ module TypeProf::Core
200
141
  @base_type
201
142
  end
202
143
 
203
- def check_match(genv, changes, vtx)
204
- vtx.each_type do |other_ty|
205
- if other_ty.is_a?(Array)
206
- if @elems.size == other_ty.elems.size
207
- match = true
208
- @elems.zip(other_ty.elems) do |elem, other_elem|
209
- unless elem.check_match(genv, changes, other_elem)
210
- match = false
211
- break
212
- end
213
- end
214
- return true if match
215
- end
216
- end
217
- end
218
- @base_type.check_match(genv, changes, vtx)
219
- end
220
-
221
144
  def show
222
145
  if @elems
223
146
  "[#{ @elems.map {|e| Type.strip_parens(e.show) }.join(", ") }]"
@@ -247,11 +170,6 @@ module TypeProf::Core
247
170
  @base_type
248
171
  end
249
172
 
250
- def check_match(genv, changes, vtx)
251
- # TODO: implement
252
- @base_type.check_match(genv, changes, vtx)
253
- end
254
-
255
173
  def show
256
174
  @base_type.show
257
175
  end
@@ -268,10 +186,6 @@ module TypeProf::Core
268
186
  genv.proc_type
269
187
  end
270
188
 
271
- def check_match(genv, changes, vtx)
272
- genv.proc_type.check_match(genv, changes, vtx)
273
- end
274
-
275
189
  def show
276
190
  "<Proc>"
277
191
  end
@@ -289,18 +203,6 @@ module TypeProf::Core
289
203
  genv.symbol_type
290
204
  end
291
205
 
292
- def check_match(genv, changes, vtx)
293
- vtx.each_type do |other_ty|
294
- case other_ty
295
- when Symbol
296
- return true if @sym == other_ty.sym
297
- when Instance
298
- return true if genv.symbol_type.check_match(genv, changes, Source.new(other_ty))
299
- end
300
- end
301
- return false
302
- end
303
-
304
206
  def show
305
207
  @sym.inspect
306
208
  end
@@ -314,10 +216,6 @@ module TypeProf::Core
314
216
  genv.obj_type
315
217
  end
316
218
 
317
- def check_match(genv, changes, vtx)
318
- return true
319
- end
320
-
321
219
  def show
322
220
  "bot"
323
221
  end
@@ -336,12 +234,43 @@ module TypeProf::Core
336
234
  genv.obj_type # Is this ok?
337
235
  end
338
236
 
339
- def check_match(genv, changes, vtx)
340
- true # should implement a better support...
237
+ def show
238
+ "var[#{ @name }]"
239
+ end
240
+ end
241
+
242
+ class Record < Type
243
+ #: (GlobalEnv, ::Hash[Symbol, Vertex], Instance) -> void
244
+ def initialize(genv, fields, base_type)
245
+ @fields = fields
246
+ @base_type = base_type
247
+ raise unless base_type.is_a?(Instance)
248
+ end
249
+
250
+ attr_reader :fields
251
+
252
+ def get_value(key = nil)
253
+ if key
254
+ # Return specific field value if it exists
255
+ @fields[key]
256
+ elsif @fields.empty?
257
+ # Empty record has no values
258
+ nil
259
+ else
260
+ # Return union of all field values if no specific key
261
+ @base_type.args[1]
262
+ end
263
+ end
264
+
265
+ def base_type(genv)
266
+ @base_type
341
267
  end
342
268
 
343
269
  def show
344
- "var[#{ @name }]"
270
+ field_strs = @fields.map do |key, val_vtx|
271
+ "#{ key }: #{ Type.strip_parens(val_vtx.show) }"
272
+ end
273
+ "{ #{ field_strs.join(", ") } }"
345
274
  end
346
275
  end
347
276
  end
data/lib/typeprof/core.rb CHANGED
@@ -23,7 +23,7 @@ require_relative "core/env/type_alias_entity"
23
23
  require_relative "core/env/value_entity"
24
24
  require_relative "core/env/method"
25
25
  require_relative "core/env/static_read"
26
- require_relative "core/graph"
26
+ require_relative "core/env/narrowing"
27
27
  require_relative "core/graph/change_set"
28
28
  require_relative "core/graph/vertex"
29
29
  require_relative "core/graph/filter"
@@ -1,18 +1,17 @@
1
1
  module TypeProf
2
2
  class Diagnostic
3
- def initialize(node, meth, msg)
3
+ def initialize(node, meth, msg, tags: nil)
4
4
  @node = node
5
5
  @meth = meth
6
6
  @msg = msg
7
- @severity = :error # TODO: keyword argument
8
- @tags = nil # TODO: keyword argument
7
+ @tags = tags
9
8
  end
10
9
 
11
10
  def reuse(new_node)
12
11
  @node = new_node
13
12
  end
14
13
 
15
- attr_reader :msg, :severity
14
+ attr_reader :node, :msg, :tags
16
15
 
17
16
  def code_range
18
17
  @node.send(@meth)
@@ -21,13 +20,13 @@ module TypeProf
21
20
  SEVERITY = { error: 1, warning: 2, info: 3, hint: 4 }
22
21
  TAG = { unnecessary: 1, deprecated: 2 }
23
22
 
24
- def to_lsp
23
+ def to_lsp(severity: :error)
25
24
  json = {
26
25
  range: code_range.to_lsp,
27
26
  source: "TypeProf",
28
27
  message: @msg,
29
28
  }
30
- json[:severity] = SEVERITY[@severity] if @severity
29
+ json[:severity] = SEVERITY[severity]
31
30
  json[:tags] = @tags.map {|tag| TAG[tag] } if @tags
32
31
  json
33
32
  end
@@ -1,5 +1,6 @@
1
1
  module TypeProf::LSP
2
2
  class Message
3
+ #: (Server, untyped) -> void
3
4
  def initialize(server, json)
4
5
  @server = server
5
6
  @id = json[:id]
@@ -28,21 +29,6 @@ module TypeProf::LSP
28
29
  @server.send_notification(method, **params)
29
30
  end
30
31
 
31
- def publish_diagnostics(uri)
32
- text = @server.open_texts[uri]
33
- diags = []
34
- if text
35
- @server.core.diagnostics(text.path) do |diag|
36
- diags << diag.to_lsp
37
- end
38
- end
39
- notify(
40
- "textDocument/publishDiagnostics",
41
- uri: uri,
42
- diagnostics: diags
43
- )
44
- end
45
-
46
32
  Classes = []
47
33
  def self.inherited(klass)
48
34
  Classes << klass
@@ -72,7 +58,7 @@ module TypeProf::LSP
72
58
  def run
73
59
  folders = @params[:workspaceFolders].map do |folder|
74
60
  folder => { uri:, }
75
- TypeProf::LSP.file_uri_to_path(uri)
61
+ @server.uri_to_path(uri)
76
62
  end
77
63
 
78
64
  @server.add_workspaces(folders)
@@ -105,7 +91,6 @@ module TypeProf::LSP
105
91
  "typeprof.disableSignature",
106
92
  ],
107
93
  },
108
- #typeDefinitionProvider: true,
109
94
  referencesProvider: true,
110
95
  },
111
96
  serverInfo: {
@@ -146,14 +131,15 @@ module TypeProf::LSP
146
131
  def run
147
132
  @params => { textDocument: { uri:, version:, text: } }
148
133
 
149
- path = TypeProf::LSP.file_uri_to_path(uri)
134
+ path = @server.uri_to_path(uri)
150
135
  return unless @server.target_path?(path)
151
136
 
152
137
  text = Text.new(path, text, version)
153
138
  @server.open_texts[uri] = text
154
- @server.core.update_rb_file(text.path, text.string)
139
+ @server.update_file(text.path, text.string)
140
+ notify("typeprof.enableToggleButton")
155
141
  @server.send_request("workspace/codeLens/refresh")
156
- publish_diagnostics(uri)
142
+ @server.publish_updated_diagnostics
157
143
  end
158
144
  end
159
145
 
@@ -161,12 +147,14 @@ module TypeProf::LSP
161
147
  METHOD = "textDocument/didChange" # notification
162
148
  def run
163
149
  @params => { textDocument: { uri:, version: }, contentChanges: changes }
150
+
164
151
  text = @server.open_texts[uri]
165
152
  return unless text
153
+
166
154
  text.apply_changes(changes, version)
167
- @server.core.update_rb_file(text.path, text.string)
155
+ @server.update_file(text.path, text.string)
168
156
  @server.send_request("workspace/codeLens/refresh")
169
- publish_diagnostics(uri)
157
+ @server.publish_updated_diagnostics
170
158
  end
171
159
  end
172
160
 
@@ -178,9 +166,11 @@ module TypeProf::LSP
178
166
  METHOD = "textDocument/didClose" # notification
179
167
  def run
180
168
  @params => { textDocument: { uri: } }
169
+
181
170
  text = @server.open_texts.delete(uri)
182
171
  return unless text
183
- @server.core.update_rb_file(text.path, nil)
172
+
173
+ @server.update_file(text.path, nil)
184
174
  end
185
175
  end
186
176
 
@@ -193,18 +183,19 @@ module TypeProf::LSP
193
183
  textDocument: { uri: },
194
184
  position: pos,
195
185
  }
186
+
196
187
  text = @server.open_texts[uri]
197
188
  unless text
198
189
  respond(nil)
199
190
  return
200
191
  end
201
- defs = @server.core.definitions(text.path, TypeProf::CodePosition.from_lsp(pos))
192
+ defs = @server.definitions(text.path, TypeProf::CodePosition.from_lsp(pos))
202
193
  if defs.empty?
203
194
  respond(nil)
204
195
  else
205
196
  respond(defs.map do |path, code_range|
206
197
  {
207
- uri: "file://" + path,
198
+ uri: @server.path_to_uri(path),
208
199
  range: code_range.to_lsp,
209
200
  }
210
201
  end)
@@ -224,13 +215,13 @@ module TypeProf::LSP
224
215
  respond(nil)
225
216
  return
226
217
  end
227
- defs = @server.core.type_definitions(text.path, TypeProf::CodePosition.from_lsp(pos))
218
+ defs = @server.type_definitions(text.path, TypeProf::CodePosition.from_lsp(pos))
228
219
  if defs.empty?
229
220
  respond(nil)
230
221
  else
231
222
  respond(defs.map do |path, code_range|
232
223
  {
233
- uri: "file://" + path,
224
+ uri: @server.path_to_uri(path),
234
225
  range: code_range.to_lsp,
235
226
  }
236
227
  end)
@@ -250,11 +241,11 @@ module TypeProf::LSP
250
241
  respond(nil)
251
242
  return
252
243
  end
253
- callsites = @server.core.references(text.path, TypeProf::CodePosition.from_lsp(pos))
244
+ callsites = @server.references(text.path, TypeProf::CodePosition.from_lsp(pos))
254
245
  if callsites
255
246
  respond(callsites.map do |path, code_range|
256
247
  {
257
- uri: "file://" + path,
248
+ uri: @server.path_to_uri(path),
258
249
  range: code_range.to_lsp,
259
250
  }
260
251
  end)
@@ -276,7 +267,7 @@ module TypeProf::LSP
276
267
  respond(nil)
277
268
  return
278
269
  end
279
- str = @server.core.hover(text.path, TypeProf::CodePosition.from_lsp(pos))
270
+ str = @server.hover(text.path, TypeProf::CodePosition.from_lsp(pos))
280
271
  if str
281
272
  respond(contents: { language: "ruby", value: str })
282
273
  else
@@ -295,7 +286,7 @@ module TypeProf::LSP
295
286
  return
296
287
  end
297
288
  ret = []
298
- @server.core.code_lens(text.path) do |code_range, title|
289
+ @server.code_lens(text.path) do |code_range, title|
299
290
  pos = code_range.first
300
291
  ret << {
301
292
  range: TypeProf::CodeRange.new(pos, pos.right).to_lsp,
@@ -332,9 +323,9 @@ module TypeProf::LSP
332
323
  items = []
333
324
  sort = "aaaa"
334
325
  text.modify_for_completion(text, pos) do |string, trigger, pos|
335
- @server.core.update_rb_file(text.path, string)
326
+ @server.update_file(text.path, string)
336
327
  pos = TypeProf::CodePosition.from_lsp(pos)
337
- @server.core.completion(text.path, trigger, pos) do |mid, hint|
328
+ @server.completion(text.path, trigger, pos) do |mid, hint|
338
329
  items << {
339
330
  label: mid,
340
331
  kind: 2, # Method
@@ -348,7 +339,7 @@ module TypeProf::LSP
348
339
  isIncomplete: false,
349
340
  items: items,
350
341
  )
351
- @server.core.update_rb_file(text.path, text.string)
342
+ @server.update_file(text.path, text.string)
352
343
  end
353
344
  end
354
345
 
@@ -369,11 +360,11 @@ module TypeProf::LSP
369
360
  respond(nil)
370
361
  return
371
362
  end
372
- renames = @server.core.rename(text.path, TypeProf::CodePosition.from_lsp(pos))
363
+ renames = @server.rename(text.path, TypeProf::CodePosition.from_lsp(pos))
373
364
  if renames
374
365
  changes = {}
375
366
  renames.each do |path, cr|
376
- (changes["file://" + path] ||= []) << {
367
+ (changes[@server.path_to_uri(path)] ||= []) << {
377
368
  range: cr.to_lsp,
378
369
  newText: newName,
379
370
  }