typeprof 0.30.1 → 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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -4
  3. data/lib/typeprof/cli/cli.rb +27 -7
  4. data/lib/typeprof/code_range.rb +9 -7
  5. data/lib/typeprof/core/ast/base.rb +27 -10
  6. data/lib/typeprof/core/ast/call.rb +85 -24
  7. data/lib/typeprof/core/ast/const.rb +7 -12
  8. data/lib/typeprof/core/ast/control.rb +345 -71
  9. data/lib/typeprof/core/ast/meta.rb +5 -5
  10. data/lib/typeprof/core/ast/method.rb +15 -5
  11. data/lib/typeprof/core/ast/misc.rb +21 -2
  12. data/lib/typeprof/core/ast/module.rb +10 -7
  13. data/lib/typeprof/core/ast/pattern.rb +9 -1
  14. data/lib/typeprof/core/ast/sig_decl.rb +163 -42
  15. data/lib/typeprof/core/ast/sig_type.rb +394 -24
  16. data/lib/typeprof/core/ast/value.rb +10 -2
  17. data/lib/typeprof/core/ast/variable.rb +32 -2
  18. data/lib/typeprof/core/ast.rb +15 -4
  19. data/lib/typeprof/core/builtin.rb +15 -9
  20. data/lib/typeprof/core/env/method.rb +21 -16
  21. data/lib/typeprof/core/env/method_entity.rb +11 -2
  22. data/lib/typeprof/core/env/module_entity.rb +57 -0
  23. data/lib/typeprof/core/env/narrowing.rb +131 -0
  24. data/lib/typeprof/core/env/static_read.rb +9 -8
  25. data/lib/typeprof/core/env.rb +37 -12
  26. data/lib/typeprof/core/graph/box.rb +207 -97
  27. data/lib/typeprof/core/graph/change_set.rb +44 -37
  28. data/lib/typeprof/core/graph/vertex.rb +4 -21
  29. data/lib/typeprof/core/service.rb +30 -1
  30. data/lib/typeprof/core/type.rb +48 -123
  31. data/lib/typeprof/core.rb +1 -0
  32. data/lib/typeprof/diagnostic.rb +5 -6
  33. data/lib/typeprof/lsp/messages.rb +21 -15
  34. data/lib/typeprof/lsp/server.rb +132 -39
  35. data/lib/typeprof/lsp/text.rb +1 -0
  36. data/lib/typeprof/version.rb +1 -1
  37. data/typeprof.conf.jsonc +22 -0
  38. data/typeprof.gemspec +1 -0
  39. metadata +19 -6
@@ -1,3 +1,6 @@
1
+ require "cgi/escape"
2
+ require "cgi/util" if RUBY_VERSION < "3.5"
3
+
1
4
  module TypeProf::LSP
2
5
  module ErrorCodes
3
6
  ParseError = -32700
@@ -8,17 +11,17 @@ module TypeProf::LSP
8
11
  end
9
12
 
10
13
  class Server
11
- def self.start_stdio(core)
14
+ def self.start_stdio(core_options)
12
15
  $stdin.binmode
13
16
  $stdout.binmode
14
17
  reader = Reader.new($stdin)
15
18
  writer = Writer.new($stdout)
16
19
  # pipe all builtin print output to stderr to avoid conflicting with lsp
17
20
  $stdout = $stderr
18
- new(core, reader, writer).run
21
+ new(core_options, reader, writer).run
19
22
  end
20
23
 
21
- def self.start_socket(core)
24
+ def self.start_socket(core_options)
22
25
  Socket.tcp_server_sockets("localhost", nil) do |servs|
23
26
  serv = servs[0].local_address
24
27
  $stdout << JSON.generate({
@@ -35,7 +38,7 @@ module TypeProf::LSP
35
38
  begin
36
39
  reader = Reader.new(sock)
37
40
  writer = Writer.new(sock)
38
- new(core, reader, writer).run
41
+ new(core_options, reader, writer).run
39
42
  ensure
40
43
  sock.close
41
44
  end
@@ -44,9 +47,9 @@ module TypeProf::LSP
44
47
  end
45
48
  end
46
49
 
47
- def initialize(core, reader, writer, url_schema: nil, publish_all_diagnostics: false)
48
- @core = core
49
- @workspaces = {}
50
+ def initialize(core_options, reader, writer, url_schema: nil)
51
+ @core_options = core_options
52
+ @cores = {}
50
53
  @reader = reader
51
54
  @writer = writer
52
55
  @request_id = 0
@@ -56,52 +59,139 @@ module TypeProf::LSP
56
59
  @exit = false
57
60
  @signature_enabled = true
58
61
  @url_schema = url_schema || (File::ALT_SEPARATOR != "\\" ? "file://" : "file:///")
59
- @publish_all_diagnostics = publish_all_diagnostics # TODO: implement more dedicated publish feature
62
+ @diagnostic_severity = :error
60
63
  end
61
64
 
62
- attr_reader :core, :open_texts
65
+ attr_reader :open_texts
63
66
  attr_accessor :signature_enabled
64
67
 
68
+ #: (String) -> String
65
69
  def path_to_uri(path)
66
- @url_schema + File.expand_path(path)
70
+ @url_schema + File.expand_path(path).split("/").map {|s| CGI.escapeURIComponent(s) }.join("/")
67
71
  end
68
72
 
69
- def uri_to_path(url)
70
- url.delete_prefix(@url_schema)
73
+ def uri_to_path(uri)
74
+ uri.delete_prefix(@url_schema).split("/").map {|s| CGI.unescapeURIComponent(s) }.join("/")
71
75
  end
72
76
 
77
+ #: (Array[String]) -> void
73
78
  def add_workspaces(folders)
74
79
  folders.each do |path|
75
- conf_path = File.join(path, "typeprof.conf.json")
76
- if File.readable?(conf_path)
77
- conf = TypeProf::LSP.load_json_with_comments(conf_path, symbolize_names: true)
78
- if conf
79
- if conf[:typeprof_version] == "experimental"
80
- if conf[:analysis_unit_dirs].size >= 2
81
- puts "currently analysis_unit_dirs can have only one directory"
82
- end
83
- conf[:analysis_unit_dirs].each do |dir|
84
- dir = File.expand_path(dir, path)
85
- @workspaces[dir] = true
86
- @core.add_workspace(dir, conf[:rbs_dir])
80
+ conf_path = [".json", ".jsonc"].map do |ext|
81
+ File.join(path, "typeprof.conf" + ext)
82
+ end.find do |path|
83
+ File.readable?(path)
84
+ end
85
+ unless conf_path
86
+ puts "typeprof.conf.json is not found in #{ path }"
87
+ next
88
+ end
89
+ conf = TypeProf::LSP.load_json_with_comments(conf_path, symbolize_names: true)
90
+ if conf
91
+ if conf[:rbs_dir]
92
+ rbs_dir = File.expand_path(conf[:rbs_dir])
93
+ else
94
+ rbs_dir = File.expand_path(File.expand_path("sig", path))
95
+ end
96
+ @rbs_dir = rbs_dir
97
+ if conf[:typeprof_version] == "experimental"
98
+ if conf[:diagnostic_severity]
99
+ severity = conf[:diagnostic_severity].to_sym
100
+ case severity
101
+ when :error, :warning, :info, :hint
102
+ @diagnostic_severity = severity
103
+ else
104
+ puts "unknown severity: #{ severity }"
87
105
  end
88
- else
89
- puts "Unknown typeprof_version: #{ conf[:typeprof_version] }"
90
106
  end
107
+ conf[:analysis_unit_dirs].each do |dir|
108
+ dir = File.expand_path(dir, path)
109
+ core = @cores[dir] = TypeProf::Core::Service.new(@core_options)
110
+ core.add_workspace(dir, @rbs_dir)
111
+ end
112
+ else
113
+ puts "Unknown typeprof_version: #{ conf[:typeprof_version] }"
91
114
  end
92
- else
93
- puts "typeprof.conf.json is not found"
94
115
  end
95
116
  end
96
117
  end
97
118
 
119
+ #: (String) -> bool
98
120
  def target_path?(path)
99
- @workspaces.each do |folder, _|
121
+ return true if @rbs_dir && path.start_with?(@rbs_dir)
122
+ @cores.each do |folder, _|
100
123
  return true if path.start_with?(folder)
101
124
  end
102
125
  return false
103
126
  end
104
127
 
128
+ def each_core(path)
129
+ @cores.each do |folder, core|
130
+ if path.start_with?(folder) || @rbs_dir && path.start_with?(@rbs_dir)
131
+ yield core
132
+ end
133
+ end
134
+ end
135
+
136
+ def aggregate_each_core(path)
137
+ ret = []
138
+ each_core(path) do |core|
139
+ r = yield(core)
140
+ ret.concat(r) if r
141
+ end
142
+ ret
143
+ end
144
+
145
+ def update_file(path, text)
146
+ each_core(path) do |core|
147
+ core.update_file(path, text)
148
+ end
149
+ end
150
+
151
+ def definitions(path, pos)
152
+ aggregate_each_core(path) do |core|
153
+ core.definitions(path, pos)
154
+ end
155
+ end
156
+
157
+ def type_definitions(path, pos)
158
+ aggregate_each_core(path) do |core|
159
+ core.type_definitions(path, pos)
160
+ end
161
+ end
162
+
163
+ def references(path, pos)
164
+ aggregate_each_core(path) do |core|
165
+ core.references(path, pos)
166
+ end
167
+ end
168
+
169
+ def hover(path, pos)
170
+ ret = []
171
+ each_core(path) do |core|
172
+ ret << core.hover(path, pos)
173
+ end
174
+ ret.compact.first # TODO
175
+ end
176
+
177
+ def code_lens(path, &blk)
178
+ each_core(path) do |core|
179
+ core.code_lens(path, &blk)
180
+ end
181
+ end
182
+
183
+ def completion(path, trigger, pos, &blk)
184
+ each_core(path) do |core|
185
+ core.completion(path, trigger, pos, &blk)
186
+ end
187
+ end
188
+
189
+ def rename(path, pos)
190
+ aggregate_each_core(path) do |core|
191
+ core.rename(path, pos)
192
+ end
193
+ end
194
+
105
195
  def run
106
196
  @reader.read do |json|
107
197
  if json[:method]
@@ -147,19 +237,22 @@ module TypeProf::LSP
147
237
  @exit = true
148
238
  end
149
239
 
150
- def publish_diagnostics(uri)
151
- (@publish_all_diagnostics ? @open_texts : [[uri, @open_texts[uri]]]).each do |uri, text|
240
+ def publish_updated_diagnostics
241
+ @cores.each do |_, core|
152
242
  diags = []
153
- if text
154
- @core.diagnostics(text.path) do |diag|
155
- diags << diag.to_lsp
243
+ core.process_diagnostic_paths do |path|
244
+ uri = path_to_uri(path)
245
+ next false unless @open_texts[uri]
246
+ core.diagnostics(path) do |diag|
247
+ diags << diag.to_lsp(severity: @diagnostic_severity)
156
248
  end
249
+ send_notification(
250
+ "textDocument/publishDiagnostics",
251
+ uri: uri,
252
+ diagnostics: diags
253
+ )
254
+ true
157
255
  end
158
- send_notification(
159
- "textDocument/publishDiagnostics",
160
- uri: uri,
161
- diagnostics: diags
162
- )
163
256
  end
164
257
  end
165
258
  end
@@ -1,5 +1,6 @@
1
1
  module TypeProf::LSP
2
2
  class Text
3
+ #: (String, String, Integer) -> void
3
4
  def initialize(path, text, version)
4
5
  @path = path
5
6
  @lines = Text.split(text)
@@ -1,3 +1,3 @@
1
1
  module TypeProf
2
- VERSION = "0.30.1"
2
+ VERSION = "0.31.0"
3
3
  end
@@ -0,0 +1,22 @@
1
+ // Experimental TypeProf configuration
2
+
3
+ // The format is completely tentative and subject to change
4
+ // (including whether to use JSON with Comments or TOML or Ruby DSL or something else)
5
+ {
6
+ // The version of TypeProf; you need to specify "experimental"
7
+ "typeprof_version": "experimental",
8
+
9
+ // The directory containing RBS files that defines the interface between the analysis units
10
+ "rbs_dir": "sig/",
11
+
12
+ // The directories containing Ruby files to be analyzed by TypeProf
13
+ // Each directory will be independently analyzed
14
+ "analysis_unit_dirs": [
15
+ "lib/typeprof/core/",
16
+ "lib/typeprof/lsp"
17
+ ],
18
+
19
+ // Severity of diagnosis, selectable from "error", "warning", "info", "hint", and "none".
20
+ // Currently, TypeProf reports many false positives, so it is recommended to use "hint" or "none".
21
+ "diagnostic_severity": "warning"
22
+ }
data/typeprof.gemspec CHANGED
@@ -28,5 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
30
 
31
+ spec.add_runtime_dependency "prism", ">= 1.4.0"
31
32
  spec.add_runtime_dependency "rbs", ">= 3.6.0"
32
33
  end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typeprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.1
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Endoh
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-23 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: prism
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 1.4.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 1.4.0
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rbs
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -64,6 +77,7 @@ files:
64
77
  - lib/typeprof/core/env/method.rb
65
78
  - lib/typeprof/core/env/method_entity.rb
66
79
  - lib/typeprof/core/env/module_entity.rb
80
+ - lib/typeprof/core/env/narrowing.rb
67
81
  - lib/typeprof/core/env/static_read.rb
68
82
  - lib/typeprof/core/env/type_alias_entity.rb
69
83
  - lib/typeprof/core/env/value_entity.rb
@@ -81,6 +95,7 @@ files:
81
95
  - lib/typeprof/lsp/text.rb
82
96
  - lib/typeprof/lsp/util.rb
83
97
  - lib/typeprof/version.rb
98
+ - typeprof.conf.jsonc
84
99
  - typeprof.gemspec
85
100
  homepage: https://github.com/ruby/typeprof
86
101
  licenses:
@@ -88,7 +103,6 @@ licenses:
88
103
  metadata:
89
104
  homepage_uri: https://github.com/ruby/typeprof
90
105
  source_code_uri: https://github.com/ruby/typeprof
91
- post_install_message:
92
106
  rdoc_options: []
93
107
  require_paths:
94
108
  - lib
@@ -103,8 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
117
  - !ruby/object:Gem::Version
104
118
  version: '0'
105
119
  requirements: []
106
- rubygems_version: 3.5.22
107
- signing_key:
120
+ rubygems_version: 3.6.9
108
121
  specification_version: 4
109
122
  summary: TypeProf is a type analysis tool for Ruby code based on abstract interpretation
110
123
  test_files: []