steep 0.29.0 → 0.33.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.
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