typeprof 0.20.1 → 0.21.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.
data/vscode/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "ruby-typeprof",
3
3
  "displayName": "Ruby TypeProf",
4
- "version": "0.20.0",
4
+ "version": "0.20.1",
5
5
  "publisher": "mame",
6
6
  "author": {
7
7
  "name": "Yusuke Endoh"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/mame/vscode-typeprof"
11
+ "url": "https://github.com/ruby/typeprof/tree/master/vscode"
12
12
  },
13
13
  "license": "MIT",
14
14
  "categories": [
@@ -62,7 +62,7 @@
62
62
  "devDependencies": {
63
63
  "@types/node": "^14.14.37",
64
64
  "typescript": "^4.2.3",
65
- "vsce": "^1.96.1",
65
+ "vsce": "^1.103.1",
66
66
  "vscode": "^1.1.37"
67
67
  },
68
68
  "dependencies": {
@@ -88,6 +88,9 @@ function executeTypeProf(folder: vscode.WorkspaceFolder, arg: String): child_pro
88
88
  if (shell && (shell.endsWith("bash") || shell.endsWith("zsh") || shell.endsWith("fish"))) {
89
89
  typeprof = child_process.spawn(shell, ["-c", "-l", cmd], { cwd });
90
90
  }
91
+ else if (process.platform === "win32") {
92
+ typeprof = child_process.spawn("C:\\Windows\\System32\\cmd.exe", ["/c", cmd], { cwd });
93
+ }
91
94
  else {
92
95
  typeprof = child_process.spawn(cmd, { cwd });
93
96
  }
@@ -95,20 +98,30 @@ function executeTypeProf(folder: vscode.WorkspaceFolder, arg: String): child_pro
95
98
  return typeprof;
96
99
  }
97
100
 
98
- function getTypeProfVersion(folder: vscode.WorkspaceFolder, callback: (version: string) => void): child_process.ChildProcessWithoutNullStreams {
101
+ function getTypeProfVersion(folder: vscode.WorkspaceFolder, outputChannel: vscode.OutputChannel, callback: (version: string) => void): child_process.ChildProcessWithoutNullStreams {
99
102
  const typeprof = executeTypeProf(folder, "--version");
100
103
  let output = "";
101
104
 
105
+ const log = (msg: string) => {
106
+ outputChannel.appendLine("[vscode] " + msg);
107
+ console.info(msg);
108
+ };
109
+
102
110
  typeprof.stdout?.on("data", out => { output += out; });
103
- typeprof.stderr?.on("data", out => { console.log(out); });
111
+ typeprof.stderr?.on("data", (out: Buffer) => {
112
+ const str = ("" + out).trim();
113
+ for (const line of str.split("\n")) {
114
+ log("stderr: " + line);
115
+ }
116
+ });
104
117
  typeprof.on("error", e => {
105
- console.info(`typeprof is not supported for this folder: ${folder}`);
106
- console.info(`because: ${e}`);
118
+ log(`typeprof is not supported for this folder: ${folder}`);
119
+ log(`because: ${e}`);
107
120
  });
108
121
  typeprof.on("exit", (code) => {
109
122
  if (code == 0) {
110
- console.info(`typeprof version: ${output}`)
111
123
  const str = output.trim();
124
+ log(`typeprof version: ${str}`)
112
125
  const version = /^typeprof (\d+).(\d+).(\d+)$/.exec(str);
113
126
  if (version) {
114
127
  const major = Number(version[1]);
@@ -118,15 +131,15 @@ function getTypeProfVersion(folder: vscode.WorkspaceFolder, callback: (version:
118
131
  callback(str);
119
132
  }
120
133
  else {
121
- console.info(`typeprof version ${str} is too old; please use 0.20.0 or later for IDE feature`);
134
+ log(`typeprof version ${str} is too old; please use 0.20.0 or later for IDE feature`);
122
135
  }
123
136
  }
124
137
  else {
125
- console.info(`typeprof --version showed unknown message`);
138
+ log(`typeprof --version showed unknown message`);
126
139
  }
127
140
  }
128
141
  else {
129
- console.info(`failed to invoke typeprof: error code ${code}`);
142
+ log(`failed to invoke typeprof: error code ${code}`);
130
143
  }
131
144
  typeprof.kill()
132
145
  });
@@ -164,7 +177,7 @@ function getTypeProfStream(folder: vscode.WorkspaceFolder, error: (msg: string)
164
177
  });
165
178
  }
166
179
 
167
- function invokeTypeProf(folder: vscode.WorkspaceFolder): LanguageClient {
180
+ function invokeTypeProf(folder: vscode.WorkspaceFolder, outputChannel: vscode.OutputChannel): LanguageClient {
168
181
  let client: LanguageClient;
169
182
 
170
183
  const reportError = (msg: string) => client.info(msg);
@@ -185,6 +198,7 @@ function invokeTypeProf(folder: vscode.WorkspaceFolder): LanguageClient {
185
198
  { scheme: "file", language: "ruby" },
186
199
  { scheme: "file", language: "rbs" },
187
200
  ],
201
+ outputChannel,
188
202
  synchronize: {
189
203
  fileEvents:
190
204
  vscode.workspace.createFileSystemWatcher("{**/*.rb,**/*.rbs}"),
@@ -199,17 +213,21 @@ function invokeTypeProf(folder: vscode.WorkspaceFolder): LanguageClient {
199
213
  const clientSessions: Map<vscode.WorkspaceFolder, State> = new Map();
200
214
 
201
215
  function startTypeProf(folder: vscode.WorkspaceFolder) {
202
- const showStatus = (msg: string) => vscode.window.setStatusBarMessage(msg, 3000);
203
- console.log(`start: ${folder}`);
216
+ const outputChannel = vscode.window.createOutputChannel("Ruby TypeProf");
217
+ const showStatus = (msg: string) => {
218
+ outputChannel.appendLine("[vscode] " + msg);
219
+ vscode.window.setStatusBarMessage(msg, 3000);
220
+ }
221
+ outputChannel.appendLine("[vscode] Try to start TypeProf for IDE");
204
222
 
205
- const typeprof = getTypeProfVersion(folder, (version) => {
223
+ const typeprof = getTypeProfVersion(folder, outputChannel, (version) => {
206
224
  if (!version) {
207
225
  showStatus(`Ruby TypeProf is not configured; Try to add "gem 'typeprof'" to Gemfile`);
208
226
  clientSessions.delete(folder);
209
227
  return;
210
228
  }
211
229
  showStatus(`Starting Ruby TypeProf (${version})...`);
212
- const client = invokeTypeProf(folder);
230
+ const client = invokeTypeProf(folder, outputChannel);
213
231
  client.onReady()
214
232
  .then(() => {
215
233
  showStatus("Ruby TypeProf is running");
@@ -225,7 +243,6 @@ function startTypeProf(folder: vscode.WorkspaceFolder) {
225
243
  }
226
244
 
227
245
  function stopTypeProf(state: State) {
228
- console.log(`stop: ${state.workspaceFolder}`);
229
246
  switch (state.kind) {
230
247
  case "invoking":
231
248
  state.process.kill();
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typeprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.1
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Endoh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-25 00:00:00.000000000 Z
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.6.2
19
+ version: 1.8.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.6.2
26
+ version: 1.8.1
27
27
  description: |
28
28
  TypeProf performs a type analysis of non-annotated Ruby code.
29
29
 
@@ -44,14 +44,6 @@ files:
44
44
  - LICENSE
45
45
  - README.md
46
46
  - Rakefile
47
- - doc/demo.md
48
- - doc/doc.ja.md
49
- - doc/doc.md
50
- - doc/ide.md
51
- - doc/ppl2019.pdf
52
- - doc/todo.md
53
- - doc/typeprof-for-ide-log.png
54
- - doc/typeprof-for-ide.png
55
47
  - exe/typeprof
56
48
  - lib/typeprof.rb
57
49
  - lib/typeprof/analyzer.rb
data/doc/demo.md DELETED
@@ -1,398 +0,0 @@
1
- # TypeProf demo cases
2
-
3
- ## A simple demo with a "User" class
4
-
5
- ```ruby
6
- def hello_message(user)
7
- "The name is " + user.name
8
- end
9
-
10
- def type_error_demo(user)
11
- "The age is " + user.age
12
- end
13
-
14
- user = User.new(name: "John", age: 20)
15
-
16
- hello_message(user)
17
- type_error_demo(user)
18
- ```
19
-
20
- ```ruby
21
- class User
22
- attr_reader name: String
23
- attr_reader age: Integer
24
-
25
- def initialize: (name: String, age: Integer) -> void
26
- end
27
- ```
28
-
29
- Result:
30
- ```
31
- $ typeprof test.rb test.rbs
32
- # Errors
33
- test.rb:6: [error] failed to resolve overload: String#+
34
-
35
- # Classes
36
- class Object
37
- def hello_message : (User) -> String
38
- def type_error_demo : (User) -> untyped
39
- end
40
- ```
41
-
42
- You can [try this analysis online](https://mame.github.io/typeprof-playground/#rb=def+hello_message%28user%29%0A++%22The+name+is+%22+%2B+user.name%0Aend%0A%0Adef+type_error_demo%28user%29%0A++%22The+age+is+%22+%2B+user.age%0Aend%0A%0Auser+%3D+User.new%28name%3A+%22John%22%2C+age%3A+20%29%0A%0Ahello_message%28user%29%0Atype_error_demo%28user%29&rbs=class+User%0A++attr_reader+name%3A+String%0A++attr_reader+age%3A+Integer%0A%0A++def+initialize%3A+%28name%3A+String%2C+age%3A+Integer%29+-%3E+void%0Aend).
43
-
44
- ## A simple demo to generate the signature prototype of "User" class
45
-
46
- ```ruby
47
- class User
48
- def initialize(name:, age:)
49
- @name, @age = name, age
50
- end
51
- attr_reader :name, :age
52
- end
53
-
54
- # A test case to tell TypeProf what types are expected by the class and methods
55
- User.new(name: "John", age: 20)
56
- ```
57
-
58
- Result:
59
- ```
60
- $ typeprof -v test.rb
61
- # Classes
62
- class User
63
- attr_reader name : String
64
- attr_reader age : Integer
65
- def initialize : (name: String, age: Integer) -> [String, Integer]
66
- end
67
- ```
68
-
69
- ## Type inspection by `p` (`Kernel#p`)
70
-
71
- ```ruby
72
- p 42 #=> Integer
73
- p "str" #=> String
74
- p "str".chars #=> Array[String]
75
- ```
76
-
77
- Result:
78
- ```
79
- $ typeprof test.rb
80
- # Revealed types
81
- # test.rb:1 #=> Integer
82
- # test.rb:2 #=> String
83
- # test.rb:3 #=> Array[String]
84
- ```
85
-
86
- ## Block with builtin methods
87
-
88
- ```ruby
89
- # TypeProf runs this block only once
90
- 10000000000000.times do |n|
91
- p n #=> Integer
92
- end
93
-
94
- # "each" with Heterogeneous array yields a union type
95
- [1, 1.0, "str"].each do |e|
96
- p e #=> Float | Integer | String
97
- end
98
-
99
- # You can use the idiom `&:method_name` too
100
- p [1, 1.0, "str"].map(&:to_s) #=> Array[String]
101
- ```
102
-
103
- ## User-defined blocks
104
-
105
- ```ruby
106
- def foo(n)
107
- yield n.to_s
108
- end
109
-
110
- foo(42) do |n|
111
- p n #=> String
112
- nil
113
- end
114
- ```
115
-
116
- Result:
117
- ```
118
- $ typeprof test.rb
119
- # Revealed types
120
- # test.rb:6 #=> String
121
-
122
- # Classes
123
- class Object
124
- def foo : (Integer) { (String) -> nil } -> nil
125
- end
126
- ```
127
-
128
- ## Arrays
129
-
130
- ```ruby
131
- # A fixed-length array literal generates a "tuple" array
132
- ary = [1, 1.0]
133
-
134
- # A tuple array keeps its length, and the association between indexes and elements
135
- p ary #=> [Integer, Float]
136
- p ary[0] #=> Integer
137
- p ary[1] #=> Float
138
-
139
- # Destructive operation is well handled (in method-local analysis)
140
- ary[0] = "str"
141
- p ary #=> [String, Float]
142
-
143
- # An calculated array generates a "sequence" array
144
- ary = [1] + [1.0]
145
-
146
- # A sequence array does not keep length nor association
147
- p ary #=> Array[Float | Integer]
148
- p ary[0] #=> Float | Integer
149
-
150
- # Destructive operation is still handled (but "weak update" is applied)
151
- ary[0] = "str"
152
- p ary #=> Array[Float | Integer | String]
153
- ```
154
-
155
- ## Multiple return values by using a tuple array
156
-
157
- ```ruby
158
- def foo
159
- return 42, "str"
160
- end
161
-
162
- int, str = foo
163
- p int #=> Integer
164
- p str #=> String
165
- ```
166
-
167
- ## Delegation by using a tuple array
168
-
169
- ```ruby
170
- def foo(x, y, z)
171
- end
172
-
173
- def proxy(dummy, *args)
174
- foo(*args)
175
- end
176
-
177
- proxy(:dummy, 1, 1.0, "str")
178
- ```
179
-
180
- ## Symbols
181
-
182
- ```ruby
183
- # Symbols are handled as concrete values instead of abstract ones
184
- p [:a, :b, :c] #=> [:a, :b, :c]
185
- ```
186
-
187
- ## Hashes
188
-
189
- ```ruby
190
- # A Hash is a "type-to-type" map
191
- h = { "int" => 1, "float" => 1.0 }
192
- p h #=> {String=>Float | Integer}
193
- p h["int"] #=> Float | Integer
194
-
195
- # Symbol-key hashes (a.k.a. records) can have distinct types for each key as Symbols are concrete
196
- h = { int: 1, float: 1.0 }
197
-
198
- p h #=> {:int=>Integer, :float=>Float}
199
- p h[:int] #=> Integer
200
- p h[:float] #=> Float
201
-
202
- # Symbol-key hash can be appropriately passed to a keyword method
203
- def foo(int:, float:)
204
- p [int, float] #=> [Integer, Float]
205
- end
206
-
207
- foo(**h)
208
- ```
209
-
210
- ## Structs
211
-
212
- ```ruby
213
- FooBar = Struct.new(:foo, :bar)
214
-
215
- obj = FooBar.new(42)
216
- obj.foo = :dummy
217
- obj.bar = "str"
218
- ```
219
-
220
- Result:
221
- ```
222
- $ typeprof test.rb
223
- # Classes
224
- class FooBar < Struct
225
- attr_accessor foo() : :dummy | Integer
226
- attr_accessor bar() : String?
227
- end
228
- ```
229
-
230
- ## Exceptions
231
-
232
- ```ruby
233
- # TypeProf assumes that any exception may be raised anywhere
234
- def foo
235
- x = 1
236
- x = "str"
237
- x = :sym
238
- ensure
239
- p(x) #=> :sym | Integer | String
240
- end
241
- ```
242
-
243
- Result:
244
- ```
245
- $ typeprof test.rb
246
- # Revealed types
247
- # test.rb:6 #=> :sym | Integer | String
248
-
249
- # Classes
250
- class Object
251
- def foo : -> :sym
252
- end
253
- ```
254
-
255
- ## RBS overloaded methods
256
-
257
- ```ruby
258
- # TypeProf selects all overloaded method declarations that matches actual arguments
259
- p foo(42) #=> Integer
260
- p foo("str") #=> String
261
- p foo(1.0) #=> failed to resolve overload: Object#foo
262
- ```
263
-
264
- ```
265
- class Object
266
- def foo: (Integer) -> Integer
267
- | (String) -> String
268
- end
269
- ```
270
-
271
- ## Flow-sensitive analysis demo: case/when with class constants
272
-
273
- ```ruby
274
- def foo(n)
275
- case n
276
- when Integer
277
- p n #=> Integer
278
- when String
279
- p n #=> String
280
- else
281
- p n #=> Float
282
- end
283
- end
284
-
285
- foo(42)
286
- foo(1.0)
287
- foo("str")
288
- ```
289
-
290
- Result:
291
- ```
292
- $ typeprof test.rb
293
- # Revealed types
294
- # test.rb:4 #=> Integer
295
- # test.rb:8 #=> Float
296
- # test.rb:6 #=> String
297
-
298
- # Classes
299
- class Object
300
- def foo : (Float | Integer | String) -> (Float | Integer | String)
301
- end
302
- ```
303
-
304
- ## Flow-sensitive analysis demo: `is_a?` and `respond_to?`
305
-
306
- ```ruby
307
- def foo(n)
308
- if n.is_a?(Integer)
309
- p n #=> Integer
310
- else
311
- p n #=> Float | String
312
- end
313
-
314
- if n.respond_to?(:times)
315
- p n #=> Integer
316
- else
317
- p n #=> Float | String
318
- end
319
- end
320
-
321
- foo(42)
322
- foo(1.0)
323
- foo("str")
324
- ```
325
-
326
- ## Flow-sensitive analysis demo: `x || y`
327
-
328
- ```ruby
329
- # ENV["FOO"] returns String? (which means String | nil)
330
- p ENV["FOO"] #=> String?
331
-
332
- # Using "|| (default value)" can force it to be non-nil
333
- p ENV["FOO"] || "default" #=> String
334
- ```
335
-
336
- ## Recursion
337
-
338
- ```ruby
339
- def fib(x)
340
- if x <= 1
341
- x
342
- else
343
- fib(x - 1) + fib(x - 2)
344
- end
345
- end
346
-
347
- fib(40000)
348
- ```
349
-
350
- Result:
351
- ```
352
- $ typeprof test.rb
353
- # Classes
354
- class Object
355
- def fib : (Integer) -> Integer
356
- end
357
- ```
358
-
359
- ## "Stub-execution" that invokes methods without tests
360
-
361
- ```ruby
362
- def foo(n)
363
- # bar is invoked with Integer arguments
364
- bar(42)
365
- n
366
- end
367
-
368
- def bar(n)
369
- n
370
- end
371
-
372
- # As there is no test code to call methods foo and bar,
373
- # TypeProf tries to invoke them with "untyped" arguments
374
- ```
375
-
376
- Result:
377
- ```
378
- $ typeprof test.rb
379
- # Classes
380
- class Object
381
- def foo : (untyped) -> untyped
382
- def bar : (Integer) -> Integer
383
- end
384
- ```
385
-
386
- ## Library demo
387
-
388
- ```ruby
389
- require "pathname"
390
-
391
- p Pathname("foo") #=> Pathname
392
- p Pathname("foo").dirname #=> Pathname
393
- p Pathname("foo").ctime #=> Time
394
- ```
395
-
396
- ## More
397
-
398
- See ruby/typeprof's [smoke](https://github.com/ruby/typeprof/tree/master/smoke) directory.