typeprof 0.20.2 → 0.21.1

Sign up to get free protection for your applications and to get access to all the features.
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.2
4
+ version: 0.21.1
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-28 00:00:00.000000000 Z
11
+ date: 2021-12-23 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.