steep 0.44.0 → 0.47.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/ruby.yml +3 -2
  4. data/.gitignore +0 -1
  5. data/CHANGELOG.md +42 -0
  6. data/Gemfile +0 -3
  7. data/Gemfile.lock +75 -0
  8. data/README.md +2 -1
  9. data/lib/steep/annotation_parser.rb +1 -1
  10. data/lib/steep/ast/builtin.rb +7 -1
  11. data/lib/steep/ast/types/factory.rb +19 -25
  12. data/lib/steep/cli.rb +7 -1
  13. data/lib/steep/diagnostic/lsp_formatter.rb +59 -6
  14. data/lib/steep/diagnostic/ruby.rb +188 -60
  15. data/lib/steep/diagnostic/signature.rb +38 -15
  16. data/lib/steep/drivers/check.rb +3 -0
  17. data/lib/steep/drivers/init.rb +10 -3
  18. data/lib/steep/drivers/utils/driver_helper.rb +15 -0
  19. data/lib/steep/drivers/validate.rb +1 -1
  20. data/lib/steep/drivers/watch.rb +3 -0
  21. data/lib/steep/equatable.rb +21 -0
  22. data/lib/steep/interface/function.rb +798 -579
  23. data/lib/steep/project/dsl.rb +135 -36
  24. data/lib/steep/project/options.rb +13 -53
  25. data/lib/steep/project/target.rb +22 -8
  26. data/lib/steep/server/interaction_worker.rb +245 -26
  27. data/lib/steep/server/master.rb +2 -2
  28. data/lib/steep/server/type_check_worker.rb +6 -9
  29. data/lib/steep/services/file_loader.rb +26 -19
  30. data/lib/steep/services/goto_service.rb +1 -0
  31. data/lib/steep/services/hover_content.rb +135 -80
  32. data/lib/steep/source.rb +12 -11
  33. data/lib/steep/type_construction.rb +435 -502
  34. data/lib/steep/type_inference/block_params.rb +3 -6
  35. data/lib/steep/type_inference/method_params.rb +483 -0
  36. data/lib/steep/type_inference/send_args.rb +599 -128
  37. data/lib/steep/typing.rb +46 -21
  38. data/lib/steep/version.rb +1 -1
  39. data/lib/steep.rb +4 -2
  40. data/sample/Steepfile +10 -3
  41. data/smoke/alias/Steepfile +2 -1
  42. data/smoke/and/Steepfile +2 -1
  43. data/smoke/array/Steepfile +2 -1
  44. data/smoke/array/test_expectations.yml +3 -3
  45. data/smoke/block/Steepfile +2 -2
  46. data/smoke/block/c.rb +0 -1
  47. data/smoke/case/Steepfile +2 -1
  48. data/smoke/class/Steepfile +2 -1
  49. data/smoke/class/test_expectations.yml +12 -15
  50. data/smoke/const/Steepfile +2 -1
  51. data/smoke/diagnostics/Steepfile +2 -1
  52. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  53. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  54. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  55. data/smoke/diagnostics/test_expectations.yml +108 -31
  56. data/smoke/diagnostics-rbs/Steepfile +1 -1
  57. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  58. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  59. data/smoke/diagnostics-rbs-duplicated/Steepfile +2 -1
  60. data/smoke/diagnostics-ruby-unsat/Steepfile +2 -1
  61. data/smoke/dstr/Steepfile +2 -1
  62. data/smoke/ensure/Steepfile +2 -1
  63. data/smoke/ensure/test_expectations.yml +3 -3
  64. data/smoke/enumerator/Steepfile +2 -1
  65. data/smoke/enumerator/test_expectations.yml +1 -1
  66. data/smoke/extension/Steepfile +2 -1
  67. data/smoke/extension/e.rbs +1 -1
  68. data/smoke/hash/Steepfile +2 -1
  69. data/smoke/hello/Steepfile +2 -1
  70. data/smoke/if/Steepfile +2 -1
  71. data/smoke/implements/Steepfile +2 -1
  72. data/smoke/initialize/Steepfile +2 -1
  73. data/smoke/integer/Steepfile +2 -1
  74. data/smoke/interface/Steepfile +2 -1
  75. data/smoke/kwbegin/Steepfile +2 -1
  76. data/smoke/lambda/Steepfile +2 -1
  77. data/smoke/literal/Steepfile +2 -1
  78. data/smoke/literal/test_expectations.yml +2 -2
  79. data/smoke/map/Steepfile +2 -1
  80. data/smoke/method/Steepfile +2 -1
  81. data/smoke/method/test_expectations.yml +11 -10
  82. data/smoke/module/Steepfile +2 -1
  83. data/smoke/regexp/Steepfile +2 -1
  84. data/smoke/regression/Steepfile +2 -1
  85. data/smoke/rescue/Steepfile +2 -1
  86. data/smoke/rescue/test_expectations.yml +3 -3
  87. data/smoke/self/Steepfile +2 -1
  88. data/smoke/skip/Steepfile +2 -1
  89. data/smoke/stdout/Steepfile +2 -1
  90. data/smoke/super/Steepfile +2 -1
  91. data/smoke/toplevel/Steepfile +2 -1
  92. data/smoke/toplevel/test_expectations.yml +3 -3
  93. data/smoke/tsort/Steepfile +4 -5
  94. data/smoke/tsort/test_expectations.yml +2 -2
  95. data/smoke/type_case/Steepfile +2 -1
  96. data/smoke/unexpected/Steepfile +2 -1
  97. data/smoke/yield/Steepfile +2 -1
  98. data/steep.gemspec +2 -2
  99. metadata +16 -10
  100. data/sig/project.rbi +0 -109
@@ -5,6 +5,10 @@ module Steep
5
5
  VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true)
6
6
  MethodCallContent = Struct.new(:node, :method_name, :type, :definition, :location, keyword_init: true)
7
7
  DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true) do
8
+ TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
9
+ ClassContent = Struct.new(:location, :decl, keyword_init: true)
10
+ InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
11
+
8
12
  def comment_string
9
13
  if comments = definition&.comments
10
14
  comments.map {|c| c.string.chomp }.uniq.join("\n----\n")
@@ -50,99 +54,150 @@ module Steep
50
54
  end
51
55
 
52
56
  def content_for(path:, line:, column:)
53
- target = project.target_for_source_path(path)
54
-
55
- if target
56
- file = service.source_files[path]
57
- typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
57
+ target_for_code, targets_for_sigs = project.targets_for_path(path)
58
58
 
59
- node, *parents = typing.source.find_nodes(line: line, column: column)
59
+ case
60
+ when target = target_for_code
61
+ Steep.logger.info "target #{target}"
60
62
 
61
- if node
62
- case node.type
63
- when :lvar
64
- var_name = node.children[0]
65
- context = typing.context_at(line: line, column: column)
66
- var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
63
+ hover_for_source(column, line, path, target)
67
64
 
68
- VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
69
- when :lvasgn
70
- var_name, rhs = node.children
71
- context = typing.context_at(line: line, column: column)
72
- type = context.lvar_env[var_name] || typing.type_of(node: rhs)
65
+ when target = targets_for_sigs[0]
66
+ service = self.service.signature_services[target.name]
73
67
 
74
- VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
75
- when :send
76
- receiver, method_name, *_ = node.children
68
+ _buffer, decls = service.latest_env.buffers_decls.find do |buffer, _|
69
+ Pathname(buffer.name) == path
70
+ end
77
71
 
72
+ return if decls.nil?
73
+
74
+ locator = RBS::Locator.new(decls: decls)
75
+ hd, tail = locator.find2(line: line, column: column)
76
+
77
+ # Maybe hover on comment
78
+ return if tail.nil?
79
+
80
+ case type = tail[0]
81
+ when RBS::Types::Alias
82
+ alias_decl = service.latest_env.alias_decls[type.name]&.decl or raise
83
+
84
+ location = tail[0].location
85
+ TypeAliasContent.new(
86
+ location: location,
87
+ decl: alias_decl
88
+ )
89
+ when RBS::Types::ClassInstance, RBS::Types::ClassSingleton
90
+ if hd == :name
91
+ env = service.latest_env
92
+ class_decl = env.class_decls[type.name]&.decls[0]&.decl or raise
93
+ location = tail[0].location[:name]
94
+ ClassContent.new(
95
+ location: location,
96
+ decl: class_decl
97
+ )
98
+ end
99
+ when RBS::Types::Interface
100
+ env = service.latest_env
101
+ interface_decl = env.interface_decls[type.name]&.decl or raise
102
+ location = type.location[:name]
103
+
104
+ InterfaceContent.new(
105
+ location: location,
106
+ decl: interface_decl
107
+ )
108
+ end
109
+ end
110
+ end
78
111
 
79
- result_node = if parents[0]&.type == :block
80
- parents[0]
112
+ def hover_for_source(column, line, path, target)
113
+ file = service.source_files[path]
114
+ typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
115
+ node, *parents = typing.source.find_nodes(line: line, column: column)
116
+
117
+ if node
118
+ case node.type
119
+ when :lvar
120
+ var_name = node.children[0]
121
+ context = typing.context_at(line: line, column: column)
122
+ var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
123
+
124
+ VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
125
+ when :lvasgn
126
+ var_name, rhs = node.children
127
+ context = typing.context_at(line: line, column: column)
128
+ type = context.lvar_env[var_name] || typing.type_of(node: rhs)
129
+
130
+ VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
131
+ when :send
132
+ receiver, method_name, *_ = node.children
133
+
134
+ result_node = case parents[0]&.type
135
+ when :block, :numblock
136
+ parents[0]
137
+ else
138
+ node
139
+ end
140
+
141
+ context = typing.context_at(line: line, column: column)
142
+
143
+ receiver_type = if receiver
144
+ typing.type_of(node: receiver)
81
145
  else
82
- node
146
+ context.self_type
83
147
  end
84
148
 
85
- context = typing.context_at(line: line, column: column)
86
-
87
- receiver_type = if receiver
88
- typing.type_of(node: receiver)
89
- else
90
- context.self_type
91
- end
92
-
93
- factory = context.type_env.subtyping.factory
94
- method_name, definition = case receiver_type
95
- when AST::Types::Name::Instance
96
- method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
97
- if method_definition&.defined_in
98
- owner_name = method_definition.defined_in
99
- [
100
- InstanceMethodName.new(owner_name, method_name),
101
- method_definition
102
- ]
103
- end
104
- when AST::Types::Name::Singleton
105
- method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
106
- if method_definition&.defined_in
107
- owner_name = method_definition.defined_in
108
- [
109
- SingletonMethodName.new(owner_name, method_name),
110
- method_definition
111
- ]
112
- end
113
- else
114
- nil
149
+ factory = context.type_env.subtyping.factory
150
+ method_name, definition = case receiver_type
151
+ when AST::Types::Name::Instance
152
+ method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
153
+ if method_definition&.defined_in
154
+ owner_name = method_definition.defined_in
155
+ [
156
+ InstanceMethodName.new(owner_name, method_name),
157
+ method_definition
158
+ ]
115
159
  end
116
-
117
- MethodCallContent.new(
118
- node: node,
119
- method_name: method_name,
120
- type: typing.type_of(node: result_node),
121
- definition: definition,
122
- location: result_node.location.expression
123
- )
124
- when :def, :defs
125
- context = typing.context_at(line: line, column: column)
126
- method_context = context.method_context
127
-
128
- if method_context && method_context.method
129
- DefinitionContent.new(
130
- node: node,
131
- method_name: method_context.name,
132
- method_type: method_context.method_type,
133
- definition: method_context.method,
134
- location: node.loc.expression
135
- )
136
- end
137
- else
138
- type = typing.type_of(node: node)
139
-
140
- TypeContent.new(
160
+ when AST::Types::Name::Singleton
161
+ method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
162
+ if method_definition&.defined_in
163
+ owner_name = method_definition.defined_in
164
+ [
165
+ SingletonMethodName.new(owner_name, method_name),
166
+ method_definition
167
+ ]
168
+ end
169
+ else
170
+ nil
171
+ end
172
+
173
+ MethodCallContent.new(
174
+ node: node,
175
+ method_name: method_name,
176
+ type: typing.type_of(node: result_node),
177
+ definition: definition,
178
+ location: result_node.location.expression
179
+ )
180
+ when :def, :defs
181
+ context = typing.context_at(line: line, column: column)
182
+ method_context = context.method_context
183
+
184
+ if method_context && method_context.method
185
+ DefinitionContent.new(
141
186
  node: node,
142
- type: type,
143
- location: node.location.expression
187
+ method_name: method_context.name,
188
+ method_type: method_context.method_type,
189
+ definition: method_context.method,
190
+ location: node.loc.expression
144
191
  )
145
192
  end
193
+ else
194
+ type = typing.type_of(node: node)
195
+
196
+ TypeContent.new(
197
+ node: node,
198
+ type: type,
199
+ location: node.location.expression
200
+ )
146
201
  end
147
202
  end
148
203
  end
data/lib/steep/source.rb CHANGED
@@ -35,28 +35,25 @@ module Steep
35
35
 
36
36
  self.emit_lambda = true
37
37
  self.emit_procarg0 = true
38
+ self.emit_kwargs = true
38
39
  end
39
40
 
40
- def self.parser
41
- ::Parser::Ruby27.new(Builder.new).tap do |parser|
41
+ def self.new_parser
42
+ ::Parser::Ruby30.new(Builder.new).tap do |parser|
42
43
  parser.diagnostics.all_errors_are_fatal = true
43
44
  parser.diagnostics.ignore_warnings = true
44
45
  end
45
46
  end
46
47
 
47
48
  def self.parse(source_code, path:, factory:)
48
- buffer = ::Parser::Source::Buffer.new(path.to_s, 1)
49
- buffer.source = source_code
50
- node = parser.parse(buffer)
49
+ buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
50
+ node = new_parser().parse(buffer)
51
51
 
52
52
  annotations = []
53
53
 
54
54
  _, comments, _ = yield_self do
55
- buffer = ::Parser::Source::Buffer.new(path.to_s)
56
- buffer.source = source_code
57
- parser = ::Parser::Ruby27.new
58
-
59
- parser.tokenize(buffer)
55
+ buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
56
+ new_parser().tokenize(buffer)
60
57
  end
61
58
 
62
59
  buffer = RBS::Buffer.new(name: path, content: source_code)
@@ -74,7 +71,11 @@ module Steep
74
71
 
75
72
  mapping = {}.compare_by_identity
76
73
 
77
- construct_mapping(node: node, annotations: annotations, mapping: mapping)
74
+ if node
75
+ construct_mapping(node: node, annotations: annotations, mapping: mapping)
76
+ else
77
+ Steep.logger.fatal { "#{path} is empty source code" }
78
+ end
78
79
 
79
80
  annotations.each do |annot|
80
81
  mapping[node] ||= []