hind 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,31 +1,14 @@
1
- # lib/hind/lsif/visitor.rb
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Hind
5
4
  module LSIF
6
- class Visitor
5
+ class Visitor < Prism::Visitor
7
6
  def initialize(generator)
8
7
  @generator = generator
9
8
  @current_scope = []
10
9
  end
11
10
 
12
- def visit(node)
13
- return unless node
14
-
15
- method_name = "visit_#{node.class.name.split("::").last.downcase}"
16
- if respond_to?(method_name)
17
- send(method_name, node)
18
- else
19
- visit_children(node)
20
- end
21
- end
22
-
23
- def visit_children(node)
24
- node.child_nodes.each { |child| visit(child) if child }
25
- end
26
-
27
- def visit_defnode(node)
28
- # Handle method definitions
11
+ def visit_def_node(node)
29
12
  method_name = node.name.to_s
30
13
  qualified_name = current_scope_name.empty? ? method_name : "#{current_scope_name}##{method_name}"
31
14
 
@@ -52,10 +35,10 @@ module Hind
52
35
 
53
36
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
54
37
 
55
- visit_children(node)
38
+ super
56
39
  end
57
40
 
58
- def visit_classnode(node)
41
+ def visit_class_node(node)
59
42
  @current_scope.push(node.constant_path.slice)
60
43
  class_name = current_scope_name
61
44
 
@@ -91,11 +74,11 @@ module Hind
91
74
  # Handle inheritance
92
75
  visit_inheritance(node.superclass) if node.superclass
93
76
 
94
- visit_children(node)
77
+ super
95
78
  @current_scope.pop
96
79
  end
97
80
 
98
- def visit_modulenode(node)
81
+ def visit_module_node(node)
99
82
  @current_scope.push(node.constant_path.slice)
100
83
  module_name = current_scope_name
101
84
 
@@ -117,11 +100,11 @@ module Hind
117
100
 
118
101
  @generator.add_to_global_state(module_name, result_set_id, range_id)
119
102
 
120
- visit_children(node)
103
+ super
121
104
  @current_scope.pop
122
105
  end
123
106
 
124
- def visit_callnode(node)
107
+ def visit_call_node(node)
125
108
  return unless node.name && node.location
126
109
 
127
110
  method_name = node.name.to_s
@@ -132,13 +115,13 @@ module Hind
132
115
 
133
116
  # Try with receiver's type if available
134
117
  if node.receiver
135
- case node.receiver
136
- when Prism::ConstantReadNode
118
+ case node.receiver.type
119
+ when :constant_read
137
120
  qualified_names << "#{node.receiver.name}##{method_name}"
138
- when Prism::CallNode
121
+ when :call
139
122
  # Handle method chaining
140
123
  qualified_names << "#{node.receiver.name}##{method_name}" if node.receiver.name
141
- when Prism::InstanceVariableReadNode
124
+ when :instance_variable_read
142
125
  # Handle instance variable calls
143
126
  qualified_names << "#{current_scope_name}##{method_name}" if current_scope_name
144
127
  end
@@ -157,10 +140,10 @@ module Hind
157
140
  break # Stop after finding first match
158
141
  end
159
142
 
160
- visit_children(node)
143
+ super
161
144
  end
162
145
 
163
- def visit_constantreadnode(node)
146
+ def visit_constant_read_node(node)
164
147
  return unless node.name
165
148
 
166
149
  constant_name = node.name.to_s
@@ -171,9 +154,11 @@ module Hind
171
154
  range_id = @generator.create_range(node.location, node.location)
172
155
  @generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
173
156
  @generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
157
+
158
+ super
174
159
  end
175
160
 
176
- def visit_constantwritenode(node)
161
+ def visit_constant_write_node(node)
177
162
  return unless node.name
178
163
 
179
164
  constant_name = node.name.to_s
@@ -197,10 +182,10 @@ module Hind
197
182
 
198
183
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
199
184
 
200
- visit_children(node)
185
+ super
201
186
  end
202
187
 
203
- def visit_instancevariablereadnode(node)
188
+ def visit_instance_variable_read_node(node)
204
189
  return unless node.name && current_scope_name
205
190
 
206
191
  var_name = node.name.to_s
@@ -211,9 +196,11 @@ module Hind
211
196
  range_id = @generator.create_range(node.location, node.location)
212
197
  @generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
213
198
  @generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
199
+
200
+ super
214
201
  end
215
202
 
216
- def visit_instancevariablewritenode(node)
203
+ def visit_instance_variable_write_node(node)
217
204
  return unless node.name && current_scope_name
218
205
 
219
206
  var_name = node.name.to_s
@@ -237,7 +224,7 @@ module Hind
237
224
 
238
225
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
239
226
 
240
- visit_children(node)
227
+ super
241
228
  end
242
229
 
243
230
  private
@@ -247,13 +234,13 @@ module Hind
247
234
  end
248
235
 
249
236
  def visit_inheritance(node)
250
- case node
251
- when Prism::ConstantReadNode, Prism::ConstantPathNode
237
+ case node.type
238
+ when :constant_read_node, :constant_path_node
252
239
  range_id = @generator.create_range(node.location, node.location)
253
- qualified_name = case node
254
- when Prism::ConstantReadNode
240
+ qualified_name = case node.type
241
+ when :constant_read_node
255
242
  node.name.to_s
256
- when Prism::ConstantPathNode
243
+ when :constant_path_node
257
244
  node.slice
258
245
  end
259
246
 
@@ -0,0 +1,239 @@
1
+ # lib/hind/lsif/visitors/declaration_visitor.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Hind
5
+ module LSIF
6
+ class DeclarationVisitor < Prism::Visitor
7
+ attr_reader :current_scope
8
+
9
+ def initialize(generator, file_path)
10
+ @generator = generator
11
+ @file_path = file_path
12
+ @current_scope = []
13
+ @current_visibility = :public
14
+ @visibility_stack = []
15
+ @in_singleton_class = false
16
+ end
17
+
18
+ def visit_class_node(node)
19
+ @current_scope.push(node.constant_path.slice)
20
+ class_name = current_scope_name
21
+
22
+ # Register class declaration
23
+ @generator.register_declaration({
24
+ type: :class,
25
+ name: class_name,
26
+ node: node,
27
+ scope: @current_scope[0..-2].join('::'),
28
+ superclass: node.superclass&.slice
29
+ })
30
+
31
+ # Process the class body with proper scope and visibility
32
+ push_visibility(:public)
33
+ super
34
+ pop_visibility
35
+ @current_scope.pop
36
+ end
37
+
38
+ def visit_module_node(node)
39
+ @current_scope.push(node.constant_path.slice)
40
+ module_name = current_scope_name
41
+
42
+ @generator.register_declaration({
43
+ type: :module,
44
+ name: module_name,
45
+ node: node,
46
+ scope: @current_scope[0..-2].join('::')
47
+ })
48
+
49
+ push_visibility(:public)
50
+ super
51
+ pop_visibility
52
+ @current_scope.pop
53
+ end
54
+
55
+ def visit_def_node(node)
56
+ method_name = node.name.to_s
57
+ # Use '#' for instance methods
58
+ qualified_name = @in_singleton_class ?
59
+ "#{current_scope_name}.#{method_name}" :
60
+ "#{current_scope_name}##{method_name}"
61
+
62
+ @generator.register_declaration({
63
+ type: :method,
64
+ name: qualified_name,
65
+ node: node,
66
+ scope: current_scope_name,
67
+ visibility: current_visibility,
68
+ params: node.parameters&.slice,
69
+ instance_method: !@in_singleton_class
70
+ })
71
+
72
+ super
73
+ end
74
+
75
+ def visit_constant_write_node(node)
76
+ return unless node.name
77
+
78
+ constant_name = node.name.to_s
79
+ qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
80
+
81
+ @generator.register_declaration({
82
+ type: :constant,
83
+ name: qualified_name,
84
+ node: node,
85
+ scope: current_scope_name
86
+ })
87
+
88
+ super
89
+ end
90
+
91
+ def visit_singleton_class_node(node)
92
+ if node.expression.is_a?(Prism::SelfNode)
93
+ # class << self
94
+ @in_singleton_class = true
95
+ super
96
+ @in_singleton_class = false
97
+ else
98
+ # Process regular singleton class
99
+ super
100
+ end
101
+ end
102
+
103
+ def visit_call_node(node)
104
+ method_name = node.name.to_s
105
+ case method_name
106
+ when 'private', 'protected', 'public'
107
+ handle_visibility_method(node)
108
+ when 'attr_reader', 'attr_writer', 'attr_accessor'
109
+ handle_attribute_method(node)
110
+ end
111
+
112
+ super
113
+ end
114
+
115
+ def visit_class_variable_write_node(node)
116
+ return unless node.name
117
+
118
+ var_name = node.name.to_s
119
+ qualified_name = "#{current_scope_name}::#{var_name}"
120
+
121
+ @generator.register_declaration({
122
+ type: :class_variable,
123
+ name: qualified_name,
124
+ node: node,
125
+ scope: current_scope_name
126
+ })
127
+
128
+ super
129
+ end
130
+
131
+ def visit_instance_variable_write_node(node)
132
+ return unless node.name && current_scope_name
133
+
134
+ var_name = node.name.to_s
135
+ qualified_name = "#{current_scope_name}##{var_name}"
136
+
137
+ @generator.register_declaration({
138
+ type: :instance_variable,
139
+ name: qualified_name,
140
+ node: node,
141
+ scope: current_scope_name
142
+ })
143
+
144
+ super
145
+ end
146
+
147
+ private
148
+
149
+ def current_scope_name
150
+ @current_scope.join('::')
151
+ end
152
+
153
+ def current_visibility
154
+ @current_visibility
155
+ end
156
+
157
+ def push_visibility(visibility)
158
+ @visibility_stack.push(@current_visibility)
159
+ @current_visibility = visibility
160
+ end
161
+
162
+ def pop_visibility
163
+ @current_visibility = @visibility_stack.pop || :public
164
+ end
165
+
166
+ def handle_visibility_method(node)
167
+ visibility = node.name.to_sym
168
+
169
+ if node.arguments&.arguments&.empty?
170
+ # Global visibility change
171
+ @current_visibility = visibility
172
+ elsif node.arguments
173
+ # Per-method visibility change
174
+ node.arguments.arguments.each do |arg|
175
+ next unless arg.is_a?(Prism::SymbolNode) || arg.is_a?(Prism::StringNode)
176
+
177
+ method_name = arg.value.to_s
178
+ qualified_name = "#{current_scope_name}##{method_name}"
179
+
180
+ if @generator.global_state.has_declaration?(qualified_name)
181
+ @generator.global_state.get_declaration(qualified_name)[:visibility] = visibility
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ def handle_attribute_method(node)
188
+ return unless node.arguments
189
+
190
+ attr_type = node.name.to_s
191
+ node.arguments.arguments.each do |arg|
192
+ next unless arg.respond_to?(:value)
193
+
194
+ attr_name = arg.value.to_s
195
+ register_attribute(attr_name, attr_type)
196
+ end
197
+ end
198
+
199
+ def register_attribute(attr_name, attr_type)
200
+ base_name = "#{current_scope_name}##{attr_name}"
201
+
202
+ # Register reader method if applicable
203
+ if %w[attr_reader attr_accessor].include?(attr_type)
204
+ @generator.register_declaration({
205
+ type: :method,
206
+ name: base_name,
207
+ node: nil, # Synthetic node
208
+ scope: current_scope_name,
209
+ visibility: current_visibility,
210
+ synthetic: true,
211
+ kind: :reader
212
+ })
213
+ end
214
+
215
+ # Register writer method if applicable
216
+ if %w[attr_writer attr_accessor].include?(attr_type)
217
+ @generator.register_declaration({
218
+ type: :method,
219
+ name: "#{base_name}=",
220
+ node: nil, # Synthetic node
221
+ scope: current_scope_name,
222
+ visibility: current_visibility,
223
+ synthetic: true,
224
+ kind: :writer
225
+ })
226
+ end
227
+
228
+ # Register the instance variable
229
+ @generator.register_declaration({
230
+ type: :instance_variable,
231
+ name: "@#{attr_name}",
232
+ node: nil, # Synthetic node
233
+ scope: current_scope_name,
234
+ synthetic: true
235
+ })
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,221 @@
1
+ # lib/hind/lsif/visitors/reference_visitor.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Hind
5
+ module LSIF
6
+ class ReferenceVisitor < Prism::Visitor
7
+ attr_reader :current_scope
8
+
9
+ def initialize(generator, file_path)
10
+ @generator = generator
11
+ @file_path = file_path
12
+ @current_scope = []
13
+ end
14
+
15
+ # Method calls
16
+ def visit_call_node(node)
17
+ return unless node.name && node.location
18
+
19
+ method_name = node.name.to_s
20
+ qualified_names = generate_qualified_names_for_call(node)
21
+
22
+ qualified_names.each do |qualified_name|
23
+ @generator.register_reference({
24
+ type: :method,
25
+ name: qualified_name,
26
+ node: node,
27
+ scope: current_scope_name,
28
+ call_type: :instance_method
29
+ })
30
+ end
31
+
32
+ super
33
+ end
34
+
35
+ # Class/module references
36
+ def visit_constant_read_node(node)
37
+ return unless node.name
38
+
39
+ constant_name = node.name.to_s
40
+ qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
41
+
42
+ @generator.register_reference({
43
+ type: :constant,
44
+ name: qualified_name,
45
+ node: node,
46
+ scope: current_scope_name
47
+ })
48
+
49
+ super
50
+ end
51
+
52
+ # Constant path references (e.g., A::B::C)
53
+ def visit_constant_path_node(node)
54
+ qualified_name = node.slice
55
+
56
+ @generator.register_reference({
57
+ type: :constant,
58
+ name: qualified_name,
59
+ node: node,
60
+ scope: current_scope_name
61
+ })
62
+
63
+ super
64
+ end
65
+
66
+ # Instance variable references
67
+ def visit_instance_variable_read_node(node)
68
+ return unless node.name && current_scope_name
69
+
70
+ var_name = node.name.to_s
71
+ qualified_name = "#{current_scope_name}##{var_name}"
72
+
73
+ @generator.register_reference({
74
+ type: :instance_variable,
75
+ name: qualified_name,
76
+ node: node,
77
+ scope: current_scope_name
78
+ })
79
+
80
+ super
81
+ end
82
+
83
+ # Class variable references
84
+ def visit_class_variable_read_node(node)
85
+ return unless node.name && current_scope_name
86
+
87
+ var_name = node.name.to_s
88
+ qualified_name = "#{current_scope_name}::#{var_name}"
89
+
90
+ @generator.register_reference({
91
+ type: :class_variable,
92
+ name: qualified_name,
93
+ node: node,
94
+ scope: current_scope_name
95
+ })
96
+
97
+ super
98
+ end
99
+
100
+ # Singleton method calls (class methods)
101
+ def visit_constant_path_call_node(node)
102
+ return unless node.name
103
+
104
+ method_name = node.name.to_s
105
+ receiver_name = node.receiver.slice
106
+ qualified_name = "#{receiver_name}.#{method_name}"
107
+
108
+ @generator.register_reference({
109
+ type: :method,
110
+ name: qualified_name,
111
+ node: node,
112
+ scope: current_scope_name,
113
+ call_type: :class_method
114
+ })
115
+
116
+ super
117
+ end
118
+
119
+ # Super method calls
120
+ def visit_super_node(node)
121
+ return unless current_scope_name
122
+
123
+ # Extract current method name from scope
124
+ current_method = current_method_name
125
+ return unless current_method
126
+
127
+ # Try to find the superclass method
128
+ if in_class_scope?
129
+ superclass = find_superclass
130
+ if superclass
131
+ qualified_name = "#{superclass}##{current_method}"
132
+
133
+ @generator.register_reference({
134
+ type: :method,
135
+ name: qualified_name,
136
+ node: node,
137
+ scope: current_scope_name,
138
+ call_type: :super
139
+ })
140
+ end
141
+ end
142
+
143
+ super
144
+ end
145
+
146
+ # Track class/module scope
147
+ def visit_class_node(node)
148
+ @current_scope.push(node.constant_path.slice)
149
+ super
150
+ @current_scope.pop
151
+ end
152
+
153
+ def visit_module_node(node)
154
+ @current_scope.push(node.constant_path.slice)
155
+ super
156
+ @current_scope.pop
157
+ end
158
+
159
+ private
160
+
161
+ def current_scope_name
162
+ @current_scope.join('::')
163
+ end
164
+
165
+ def generate_qualified_names_for_call(node)
166
+ qualified_names = []
167
+ method_name = node.name.to_s
168
+
169
+ # Try with current scope first
170
+ qualified_names << "#{current_scope_name}##{method_name}" unless current_scope_name.empty?
171
+
172
+ # Try with receiver's type if available
173
+ if node.receiver
174
+ case node.receiver
175
+ when Prism::ConstantReadNode
176
+ qualified_names << "#{node.receiver.name}##{method_name}"
177
+ when Prism::ConstantPathNode
178
+ qualified_names << "#{node.receiver.slice}##{method_name}"
179
+ when Prism::CallNode
180
+ # Method chaining - try both instance and class methods
181
+ if node.receiver.name
182
+ qualified_names << "#{node.receiver.name}##{method_name}"
183
+ qualified_names << "#{node.receiver.name}.#{method_name}"
184
+ end
185
+ when Prism::InstanceVariableReadNode
186
+ # Instance variable calls - try current class context
187
+ qualified_names << "#{current_scope_name}##{method_name}" if current_scope_name
188
+ end
189
+ end
190
+
191
+ # Try as a standalone method
192
+ qualified_names << method_name
193
+
194
+ # Add potential class method variant
195
+ qualified_names << "#{current_scope_name}.#{method_name}" unless current_scope_name.empty?
196
+
197
+ qualified_names.uniq
198
+ end
199
+
200
+ def current_method_name
201
+ # Try to find the nearest method node in the AST
202
+ # This is a simplified version - you might need to enhance this
203
+ # based on your specific needs
204
+ "current_method"
205
+ end
206
+
207
+ def in_class_scope?
208
+ # Check if we're currently in a class definition
209
+ !@current_scope.empty? && @generator.global_state.declarations[@current_scope.last]&.[](:type) == :class
210
+ end
211
+
212
+ def find_superclass
213
+ return unless in_class_scope?
214
+
215
+ current_class = @current_scope.last
216
+ class_declaration = @generator.global_state.declarations[current_class]
217
+ class_declaration&.[](:superclass)
218
+ end
219
+ end
220
+ end
221
+ end
data/lib/hind/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hind
4
- VERSION = '0.1.5'
4
+ VERSION = '0.1.6'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aboobacker MK
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-09 00:00:00.000000000 Z
10
+ date: 2025-02-10 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: prism
@@ -112,6 +112,8 @@ files:
112
112
  - lib/hind/lsif/global_state.rb
113
113
  - lib/hind/lsif/vertex.rb
114
114
  - lib/hind/lsif/visitor.rb
115
+ - lib/hind/lsif/visitors/declaration_visitor.rb
116
+ - lib/hind/lsif/visitors/reference_visitor.rb
115
117
  - lib/hind/parser.rb
116
118
  - lib/hind/scip.rb
117
119
  - lib/hind/scip/generator.rb