steep 0.29.0 → 0.33.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d09c66d40509db4956625f6be82477c4cda1b375d4cc37ebc2b4fdfdfdd971f
4
- data.tar.gz: bfe074e9fc6e833faa4442f718e2bade7e54a5b6921fd8cf63465972ec39ced5
3
+ metadata.gz: e93086efe3704ac8b59a1450fda301b7ab91f409c13c53a679a2ed3b3419ca84
4
+ data.tar.gz: b00e2030a9ea2171b6f967c78f469204c903cb937d421e5dcab896021fce2919
5
5
  SHA512:
6
- metadata.gz: 05d54b1565ccf9189d335163d952d075bd6f7b3a08c26362efb815c3d19cfd2ffa2eb13e124a5dc7a4a5f1343a6fbbe5694ed3d6000a4fe37f3ace4cb2d7c1d5
7
- data.tar.gz: 2661d5a8a5273e5c4341b27fd78a5f57c296c31663f16d46bf8de39ea1af40b827bfdf48f9266e071a37b1fe395a58ec716149d88656cf983587ecbe89fa24e6
6
+ metadata.gz: 06023d31b509f58efdea8c8f0e13c49b38f3a4936debbe3a0d88bfd84b356e7a761506a8f105c035bd454a4b2f39ae6af13d16b84d29e6d0050d9d58d7062e82
7
+ data.tar.gz: '0601459d31ad8ca758c95c29215eb237b5bc50490b5133e574057ec1836396d9189c3dd22628244c1d8d432e0f945a746c280ae96ca20b768c063fbae0c696af'
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.33.0 (2020-10-13)
6
+
7
+ * Make `!` typing flow sensitive ([#240](https://github.com/soutaro/steep/pull/240))
8
+
9
+ ## 0.32.0 (2020-10-09)
10
+
11
+ * Let type-case support interface types ([#237](https://github.com/soutaro/steep/pull/237))
12
+
13
+ ## 0.31.1 (2020-10-07)
14
+
15
+ * Fix `if-then-else` parsing ([#236](https://github.com/soutaro/steep/pull/236))
16
+
17
+ ## 0.31.0 (2020-10-04)
18
+
19
+ * Fix type checking performance ([#230](https://github.com/soutaro/steep/pull/230))
20
+ * Improve LSP completion/hover performance ([#232](https://github.com/soutaro/steep/pull/232))
21
+ * Fix instance variable completion ([#234](https://github.com/soutaro/steep/pull/234))
22
+ * Relax version requirements on Listen to allow installing on Ruby 3 ([#235](https://github.com/soutaro/steep/pull/235))
23
+
24
+ ## 0.30.0 (2020-10-03)
25
+
26
+ * Let top-level defs be methods of Object ([#227](https://github.com/soutaro/steep/pull/227))
27
+ * Fix error caused by attribute definitions ([#228](https://github.com/soutaro/steep/pull/228))
28
+ * LSP worker improvements ([#222](https://github.com/soutaro/steep/pull/222), [#223](https://github.com/soutaro/steep/pull/223), [#226](https://github.com/soutaro/steep/pull/226), [#229](https://github.com/soutaro/steep/pull/229))
29
+
5
30
  ## 0.29.0 (2020-09-28)
6
31
 
7
32
  * Implement reasoning on `is_a?`, `nil?`, and `===` methods. ([#218](https://github.com/soutaro/steep/pull/218))
@@ -9,8 +9,7 @@ def exit(*)
9
9
 
10
10
  end
11
11
 
12
-
13
12
  STDERR.puts "Running profiler: mode=#{mode}, out=#{out}"
14
- StackProf.run(mode: mode, out: out) do
13
+ StackProf.run(mode: mode, out: out, raw: true) do
15
14
  load File.join(__dir__, "../exe/steep")
16
15
  end
@@ -16,7 +16,6 @@ require 'uri'
16
16
 
17
17
  require "rbs"
18
18
 
19
- require "steep/ast/location"
20
19
  require "steep/ast/types/helper"
21
20
  require "steep/ast/types/any"
22
21
  require "steep/ast/types/instance"
@@ -39,7 +38,6 @@ require "steep/ast/types/logic"
39
38
  require "steep/ast/type_params"
40
39
  require "steep/ast/annotation"
41
40
  require "steep/ast/annotation/collection"
42
- require "steep/ast/buffer"
43
41
  require "steep/ast/builtin"
44
42
  require "steep/ast/types/factory"
45
43
 
@@ -83,7 +81,8 @@ require "steep/server/interaction_worker"
83
81
  require "steep/server/master"
84
82
 
85
83
  require "steep/project"
86
- require "steep/project/file"
84
+ require "steep/project/signature_file"
85
+ require "steep/project/source_file"
87
86
  require "steep/project/options"
88
87
  require "steep/project/target"
89
88
  require "steep/project/dsl"
@@ -7,11 +7,14 @@ module Steep
7
7
  attr_reader :type_name_cache
8
8
  attr_reader :type_cache
9
9
 
10
+ attr_reader :type_interface_cache
11
+
10
12
  def initialize(builder:)
11
13
  @definition_builder = builder
12
14
 
13
15
  @type_name_cache = {}
14
16
  @type_cache = {}
17
+ @type_interface_cache = {}
15
18
  end
16
19
 
17
20
  def type_name_resolver
@@ -339,46 +342,48 @@ module Steep
339
342
  end
340
343
  end
341
344
 
345
+ NilClassName = TypeName("::NilClass")
346
+
342
347
  def setup_primitives(method_name, method_type)
343
348
  if method_def = method_type.method_def
344
349
  defined_in = method_def.defined_in
345
350
  member = method_def.member
346
351
 
347
- case
348
- when defined_in == RBS::BuiltinNames::Object.name && member.instance?
352
+ if member.is_a?(RBS::AST::Members::MethodDefinition)
349
353
  case method_name
350
354
  when :is_a?, :kind_of?, :instance_of?
351
- return method_type.with(
352
- return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
353
- )
354
- when :nil?
355
- return method_type.with(
356
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
357
- )
358
- end
355
+ if defined_in == RBS::BuiltinNames::Object.name && member.instance?
356
+ return method_type.with(
357
+ return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
358
+ )
359
+ end
359
360
 
360
- when defined_in == AST::Builtin::NilClass.module_name && member.instance?
361
- case method_name
362
361
  when :nil?
363
- return method_type.with(
364
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
365
- )
366
- end
362
+ case defined_in
363
+ when RBS::BuiltinNames::Object.name,
364
+ NilClassName
365
+ return method_type.with(
366
+ return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
367
+ )
368
+ end
367
369
 
368
- when defined_in == RBS::BuiltinNames::BasicObject.name && member.instance?
369
- case method_name
370
370
  when :!
371
- return method_type.with(
372
- return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
373
- )
374
- end
371
+ case defined_in
372
+ when RBS::BuiltinNames::BasicObject.name,
373
+ RBS::BuiltinNames::TrueClass.name,
374
+ RBS::BuiltinNames::FalseClass.name
375
+ return method_type.with(
376
+ return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
377
+ )
378
+ end
375
379
 
376
- when defined_in == RBS::BuiltinNames::Module.name && member.instance?
377
- case method_name
378
380
  when :===
379
- return method_type.with(
380
- return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
381
- )
381
+ case defined_in
382
+ when RBS::BuiltinNames::Module.name
383
+ return method_type.with(
384
+ return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
385
+ )
386
+ end
382
387
  end
383
388
  end
384
389
  end
@@ -388,15 +393,23 @@ module Steep
388
393
 
389
394
  def interface(type, private:, self_type: type)
390
395
  Steep.logger.debug { "Factory#interface: #{type}, private=#{private}, self_type=#{self_type}" }
391
- type = expand_alias(type)
396
+
397
+ cache_key = [type, self_type, private]
398
+ if type_interface_cache.key?(cache_key)
399
+ return type_interface_cache[cache_key]
400
+ end
392
401
 
393
402
  case type
403
+ when Name::Alias
404
+ interface(expand_alias(type), private: private, self_type: self_type)
405
+
394
406
  when Self
395
407
  if self_type != type
396
408
  interface self_type, private: private, self_type: Self.new
397
409
  else
398
410
  raise "Unexpected `self` type interface"
399
411
  end
412
+
400
413
  when Name::Instance
401
414
  Interface::Interface.new(type: self_type, private: private).tap do |interface|
402
415
  definition = definition_builder.build_instance(type.name)
@@ -687,6 +700,8 @@ module Steep
687
700
 
688
701
  else
689
702
  raise "Unexpected type for interface: #{type}"
703
+ end.tap do |interface|
704
+ type_interface_cache[cache_key] = interface
690
705
  end
691
706
  end
692
707
 
@@ -24,6 +24,31 @@ module Steep
24
24
  (base_dir + path).cleanpath
25
25
  end
26
26
 
27
+ def target_for_source_path(path)
28
+ targets.find do |target|
29
+ target.possible_source_file?(path)
30
+ end
31
+ end
32
+
33
+ def targets_for_path(path)
34
+ if target = target_for_source_path(path)
35
+ [target, []]
36
+ else
37
+ [
38
+ nil,
39
+ targets.select do |target|
40
+ target.possible_signature_file?(path)
41
+ end
42
+ ]
43
+ end
44
+ end
45
+
46
+ def all_source_files
47
+ targets.each.with_object(Set[]) do |target, paths|
48
+ paths.merge(target.source_files.keys)
49
+ end
50
+ end
51
+
27
52
  def type_of_node(path:, line:, column:)
28
53
  source_file = targets.map {|target| target.source_files[path] }.compact[0]
29
54
 
@@ -29,11 +29,13 @@ module Steep
29
29
  @subtyping = subtyping
30
30
  end
31
31
 
32
- def type_check!(text)
32
+ def type_check!(text, line:, column:)
33
33
  @modified_text = text
34
34
 
35
35
  Steep.measure "parsing" do
36
- @source = SourceFile.parse(text, path: path, factory: subtyping.factory)
36
+ @source = SourceFile
37
+ .parse(text, path: path, factory: subtyping.factory)
38
+ .without_unrelated_defs(line: line, column: column)
37
39
  end
38
40
 
39
41
  Steep.measure "typechecking" do
@@ -53,7 +55,7 @@ module Steep
53
55
  begin
54
56
  Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
55
57
  Steep.measure "type_check!" do
56
- type_check!(source_text)
58
+ type_check!(source_text, line: line, column: column)
57
59
  end
58
60
  end
59
61
 
@@ -66,11 +68,11 @@ module Steep
66
68
  case possible_trigger
67
69
  when "."
68
70
  source_text[index-1] = " "
69
- type_check!(source_text)
71
+ type_check!(source_text, line: line, column: column)
70
72
  items_for_dot(position: position)
71
73
  when "@"
72
74
  source_text[index-1] = " "
73
- type_check!(source_text)
75
+ type_check!(source_text, line: line, column: column)
74
76
  items_for_atmark(position: position)
75
77
  else
76
78
  []
@@ -219,14 +221,14 @@ module Steep
219
221
  def items_for_atmark(position:)
220
222
  # @ ←
221
223
  shift_pos = position-1
222
- node, *parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
224
+ node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
223
225
  node ||= source.node
224
226
 
225
227
  return [] unless node
226
228
 
227
229
  context = typing.context_at(line: position.line, column: position.column)
228
230
  items = []
229
- instance_variable_items_for_context(context, prefix: "", position: position, items: items)
231
+ instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
230
232
  items
231
233
  end
232
234
 
@@ -28,15 +28,20 @@ module Steep
28
28
  end
29
29
 
30
30
  def load_sources(command_line_patterns)
31
+ loaded_paths = Set[]
32
+
31
33
  project.targets.each do |target|
32
34
  Steep.logger.tagged "target=#{target.name}" do
33
35
  target_patterns = command_line_patterns.empty? ? target.source_patterns : command_line_patterns
34
36
 
35
- each_path_in_patterns target_patterns, ".rb" do |path|
37
+ each_path_in_patterns(target_patterns, ".rb") do |path|
36
38
  if target.possible_source_file?(path)
37
- unless target.source_file?(path)
39
+ if loaded_paths.include?(path)
40
+ Steep.logger.warn { "Skipping #{target} while loading #{path}... (Already loaded to another target.)" }
41
+ else
38
42
  Steep.logger.info { "Adding source file: #{path}" }
39
43
  target.add_source path, project.absolute_path(path).read
44
+ loaded_paths << path
40
45
  end
41
46
  end
42
47
  end
@@ -36,102 +36,113 @@ module Steep
36
36
  end
37
37
  end
38
38
 
39
+ def typecheck(target, path:, line:, column:)
40
+ target.type_check(target_sources: [], validate_signatures: false)
41
+
42
+ case (status = target.status)
43
+ when Project::Target::TypeCheckStatus
44
+ subtyping = status.subtyping
45
+ source = SourceFile
46
+ .parse(target.source_files[path].content, path: path, factory: subtyping.factory)
47
+ .without_unrelated_defs(line: line, column: column)
48
+ SourceFile.type_check(source, subtyping: subtyping)
49
+ end
50
+ rescue
51
+ nil
52
+ end
53
+
39
54
  def content_for(path:, line:, column:)
40
- target = project.targets.find {|target| target.source_file?(path) }
55
+ target = project.target_for_source_path(path)
41
56
 
42
57
  if target
43
- source_file = target.source_files[path]
44
- target.type_check(target_sources: [source_file], validate_signatures: false)
58
+ typing = typecheck(target, path: path, line: line, column: column) or return
59
+
60
+ node, *parents = typing.source.find_nodes(line: line, column: column)
61
+
62
+ if node
63
+ case node.type
64
+ when :lvar
65
+ var_name = node.children[0]
66
+ context = typing.context_at(line: line, column: column)
67
+ var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
45
68
 
46
- case (status = source_file.status)
47
- when SourceFile::TypeCheckStatus
48
- node, *parents = status.source.find_nodes(line: line, column: column)
69
+ VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
70
+ when :lvasgn
71
+ var_name, rhs = node.children
72
+ context = typing.context_at(line: line, column: column)
73
+ type = context.lvar_env[var_name.name] || typing.type_of(node: rhs)
49
74
 
50
- if node
51
- case node.type
52
- when :lvar
53
- var_name = node.children[0]
54
- context = status.typing.context_at(line: line, column: column)
55
- var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
75
+ VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
76
+ when :send
77
+ receiver, method_name, *_ = node.children
56
78
 
57
- VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
58
- when :lvasgn
59
- var_name, rhs = node.children
60
- context = status.typing.context_at(line: line, column: column)
61
- type = context.lvar_env[var_name.name] || status.typing.type_of(node: rhs)
62
79
 
63
- VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
64
- when :send
65
- receiver, method_name, *_ = node.children
80
+ result_node = if parents[0]&.type == :block
81
+ parents[0]
82
+ else
83
+ node
84
+ end
66
85
 
86
+ context = typing.context_at(line: line, column: column)
67
87
 
68
- result_node = if parents[0]&.type == :block
69
- parents[0]
88
+ receiver_type = if receiver
89
+ typing.type_of(node: receiver)
70
90
  else
71
- node
91
+ context.self_type
72
92
  end
73
93
 
74
- context = status.typing.context_at(line: line, column: column)
75
-
76
- receiver_type = if receiver
77
- status.typing.type_of(node: receiver)
78
- else
79
- context.self_type
80
- end
81
-
82
- factory = context.type_env.subtyping.factory
83
- method_name, definition = case receiver_type
84
- when AST::Types::Name::Instance
85
- method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
86
- if method_definition&.defined_in
87
- owner_name = method_definition.defined_in
88
- [
89
- InstanceMethodName.new(owner_name, method_name),
90
- method_definition
91
- ]
92
- end
93
- when AST::Types::Name::Singleton
94
- method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
95
- if method_definition&.defined_in
96
- owner_name = method_definition.defined_in
97
- [
98
- SingletonMethodName.new(owner_name, method_name),
99
- method_definition
100
- ]
101
- end
102
- else
103
- nil
94
+ factory = context.type_env.subtyping.factory
95
+ method_name, definition = case receiver_type
96
+ when AST::Types::Name::Instance
97
+ method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
98
+ if method_definition&.defined_in
99
+ owner_name = method_definition.defined_in
100
+ [
101
+ InstanceMethodName.new(owner_name, method_name),
102
+ method_definition
103
+ ]
104
104
  end
105
-
106
- MethodCallContent.new(
107
- node: node,
108
- method_name: method_name,
109
- type: status.typing.type_of(node: result_node),
110
- definition: definition,
111
- location: result_node.location.expression
112
- )
113
- when :def, :defs
114
- context = status.typing.context_at(line: line, column: column)
115
- method_context = context.method_context
116
-
117
- if method_context && method_context.method
118
- DefinitionContent.new(
119
- node: node,
120
- method_name: method_context.name,
121
- method_type: method_context.method_type,
122
- definition: method_context.method,
123
- location: node.loc.expression
124
- )
125
- end
126
- else
127
- type = status.typing.type_of(node: node)
128
-
129
- TypeContent.new(
105
+ when AST::Types::Name::Singleton
106
+ method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
107
+ if method_definition&.defined_in
108
+ owner_name = method_definition.defined_in
109
+ [
110
+ SingletonMethodName.new(owner_name, method_name),
111
+ method_definition
112
+ ]
113
+ end
114
+ else
115
+ nil
116
+ end
117
+
118
+ MethodCallContent.new(
119
+ node: node,
120
+ method_name: method_name,
121
+ type: typing.type_of(node: result_node),
122
+ definition: definition,
123
+ location: result_node.location.expression
124
+ )
125
+ when :def, :defs
126
+ context = typing.context_at(line: line, column: column)
127
+ method_context = context.method_context
128
+
129
+ if method_context && method_context.method
130
+ DefinitionContent.new(
130
131
  node: node,
131
- type: type,
132
- location: node.location.expression
132
+ method_name: method_context.name,
133
+ method_type: method_context.method_type,
134
+ definition: method_context.method,
135
+ location: node.loc.expression
133
136
  )
134
137
  end
138
+ else
139
+ type = typing.type_of(node: node)
140
+
141
+ TypeContent.new(
142
+ node: node,
143
+ type: type,
144
+ location: node.location.expression
145
+ )
135
146
  end
136
147
  end
137
148
  end