type-guessr 0.0.1

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.
@@ -0,0 +1,304 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+ require "rbs"
5
+ require_relative "types"
6
+ require_relative "logger"
7
+ require_relative "converter/rbs_converter"
8
+
9
+ module TypeGuessr
10
+ module Core
11
+ # RBSProvider loads and queries RBS signature information
12
+ # Provides lazy loading of RBS environment for method signatures
13
+ # Uses RBSConverter to convert RBS types to internal type system
14
+ class RBSProvider
15
+ include Singleton
16
+
17
+ # Represents a method signature from RBS
18
+ class Signature
19
+ attr_reader :method_type
20
+
21
+ def initialize(method_type)
22
+ @method_type = method_type
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ @env = nil
28
+ @loader = nil
29
+ @converter = Converter::RBSConverter.new
30
+ end
31
+
32
+ # Preload RBS environment (for eager loading during addon activation)
33
+ # @return [self]
34
+ def preload
35
+ ensure_environment_loaded
36
+ self
37
+ end
38
+
39
+ # Get method signatures for a class and method name
40
+ # @param class_name [String] the class name
41
+ # @param method_name [String] the method name
42
+ # @return [Array<Signature>] array of method signatures
43
+ def get_method_signatures(class_name, method_name)
44
+ ensure_environment_loaded
45
+
46
+ # Build the type name
47
+ type_name = build_type_name(class_name)
48
+
49
+ # Check if class exists in RBS before building (avoids RuntimeError for project classes)
50
+ return [] unless @env.class_decl?(type_name)
51
+
52
+ # Use RBS::DefinitionBuilder to get method definitions
53
+ builder = RBS::DefinitionBuilder.new(env: @env)
54
+ definition = builder.build_instance(type_name)
55
+
56
+ # Get the method definition
57
+ method_def = definition.methods[method_name.to_sym]
58
+ return [] unless method_def
59
+
60
+ # Return all method types (overloads)
61
+ method_def.method_types.map { |mt| Signature.new(mt) }
62
+ rescue RBS::NoTypeFoundError, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => _e
63
+ # Class not found in RBS
64
+ []
65
+ rescue StandardError => e
66
+ # If anything goes wrong, return empty array
67
+ Logger.error("RBSProvider error", e)
68
+ []
69
+ end
70
+
71
+ # Get the return type of a method call with overload resolution
72
+ # @param class_name [String] the receiver class name
73
+ # @param method_name [String] the method name
74
+ # @param arg_types [Array<Types::Type>] argument types for overload matching
75
+ # @return [Types::Type] the return type (Unknown if not found)
76
+ def get_method_return_type(class_name, method_name, arg_types = [])
77
+ signatures = get_method_signatures(class_name, method_name)
78
+ return Types::Unknown.instance if signatures.empty?
79
+
80
+ # Find best matching overload based on argument types
81
+ best_match = find_best_overload(signatures, arg_types)
82
+ return_type = best_match.method_type.type.return_type
83
+
84
+ @converter.convert(return_type)
85
+ end
86
+
87
+ # Get block parameter types for a method
88
+ # @param class_name [String] the receiver class name
89
+ # @param method_name [String] the method name
90
+ # @return [Array<Types::Type>] array of block parameter types (empty if no block)
91
+ def get_block_param_types(class_name, method_name)
92
+ block_sig = find_block_signature(class_name, method_name)
93
+ return [] unless block_sig
94
+
95
+ extract_block_param_types(block_sig)
96
+ end
97
+
98
+ # Get class method signatures (singleton methods like File.read, Array.new)
99
+ # @param class_name [String] the class name
100
+ # @param method_name [String] the method name
101
+ # @return [Array<Signature>] array of method signatures
102
+ def get_class_method_signatures(class_name, method_name)
103
+ ensure_environment_loaded
104
+
105
+ # Build the type name
106
+ type_name = build_type_name(class_name)
107
+
108
+ # Check if class exists in RBS before building (avoids RuntimeError for project classes)
109
+ return [] unless @env.class_decl?(type_name)
110
+
111
+ # Use RBS::DefinitionBuilder to get singleton method definitions
112
+ builder = RBS::DefinitionBuilder.new(env: @env)
113
+ definition = builder.build_singleton(type_name)
114
+
115
+ # Get the method definition
116
+ method_def = definition.methods[method_name.to_sym]
117
+ return [] unless method_def
118
+
119
+ # Return all method types (overloads)
120
+ method_def.method_types.map { |mt| Signature.new(mt) }
121
+ rescue RBS::NoTypeFoundError, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => _e
122
+ # Class not found in RBS
123
+ []
124
+ rescue StandardError => e
125
+ # If anything goes wrong, return empty array
126
+ Logger.error("RBSProvider class method error", e)
127
+ []
128
+ end
129
+
130
+ # Get the return type of a class method call
131
+ # @param class_name [String] the class name
132
+ # @param method_name [String] the method name
133
+ # @param arg_types [Array<Types::Type>] the argument types
134
+ # @return [Types::Type] the return type (Unknown if not found)
135
+ def get_class_method_return_type(class_name, method_name, arg_types = [])
136
+ signatures = get_class_method_signatures(class_name, method_name)
137
+ return Types::Unknown.instance if signatures.empty?
138
+
139
+ # Find best matching overload based on argument types
140
+ best_match = find_best_overload(signatures, arg_types)
141
+ return_type = best_match.method_type.type.return_type
142
+
143
+ @converter.convert(return_type)
144
+ end
145
+
146
+ private
147
+
148
+ # Find a method signature that has a block
149
+ # @param class_name [String] the receiver class name
150
+ # @param method_name [String] the method name
151
+ # @return [RBS::MethodType, nil] the method type with block, or nil
152
+ def find_block_signature(class_name, method_name)
153
+ signatures = get_method_signatures(class_name, method_name)
154
+ return nil if signatures.empty?
155
+
156
+ # Find the signature with a block
157
+ sig_with_block = signatures.find { |s| s.method_type.block }
158
+ sig_with_block&.method_type
159
+ end
160
+
161
+ # Extract block parameter types from a method type
162
+ # @param method_type [RBS::MethodType] the method type
163
+ # @param substitutions [Hash{Symbol => Types::Type}] type variable substitutions (e.g., { Elem: Integer })
164
+ # @return [Array<Types::Type>] array of parameter types
165
+ def extract_block_param_types(method_type, substitutions: {})
166
+ return [] unless method_type.block
167
+
168
+ block_func = method_type.block.type
169
+ block_func.required_positionals.flat_map do |param|
170
+ # Handle Tuple types (e.g., [K, V] in Hash#each) by flattening
171
+ raw_types = if param.type.is_a?(RBS::Types::Tuple)
172
+ param.type.types.map { |t| @converter.convert(t) }
173
+ else
174
+ [@converter.convert(param.type)]
175
+ end
176
+ # Apply substitutions after conversion
177
+ raw_types.map { |t| t.substitute(substitutions) }
178
+ end
179
+ end
180
+
181
+ def ensure_environment_loaded
182
+ return if @env
183
+
184
+ @loader = RBS::EnvironmentLoader.new
185
+
186
+ # Add optional library paths if they exist
187
+ # loader.add(path: Pathname("sig")) if Dir.exist?("sig")
188
+
189
+ # Load environment (this automatically loads core/stdlib)
190
+ @env = RBS::Environment.from_loader(@loader).resolve_type_names
191
+ rescue StandardError => e
192
+ Logger.error("Failed to load RBS environment", e)
193
+ # Fallback to empty environment
194
+ @env = RBS::Environment.new
195
+ end
196
+
197
+ def build_type_name(class_name)
198
+ # Parse class name to handle namespaced classes like "Foo::Bar"
199
+ parts = class_name.split("::")
200
+ namespace_parts = parts[0...-1]
201
+ name = parts.last
202
+
203
+ namespace = if namespace_parts.empty?
204
+ RBS::Namespace.root
205
+ else
206
+ RBS::Namespace.parse(namespace_parts.join("::"))
207
+ end
208
+
209
+ RBS::TypeName.new(name: name.to_sym, namespace: namespace)
210
+ end
211
+
212
+ # Find the best matching overload for given argument types
213
+ # @param signatures [Array<Signature>] available overloads
214
+ # @param arg_types [Array<Types::Type>] argument types
215
+ # @return [Signature] best matching signature (first if no match)
216
+ def find_best_overload(signatures, arg_types)
217
+ return signatures.first if arg_types.empty?
218
+
219
+ # Score each overload
220
+ scored = signatures.map do |sig|
221
+ score = calculate_overload_score(sig.method_type, arg_types)
222
+ [sig, score]
223
+ end
224
+
225
+ # Return best scoring overload, or first if all scores are 0
226
+ best = scored.max_by { |_sig, score| score }
227
+ best[1].positive? ? best[0] : signatures.first
228
+ end
229
+
230
+ # Calculate match score for an overload
231
+ # @param method_type [RBS::MethodType] the method type
232
+ # @param arg_types [Array<Types::Type>] argument types
233
+ # @return [Integer] score (higher = better match)
234
+ def calculate_overload_score(method_type, arg_types)
235
+ func = method_type.type
236
+ required = func.required_positionals
237
+ optional = func.optional_positionals
238
+ rest = func.rest_positionals
239
+
240
+ # Check argument count
241
+ min_args = required.size
242
+ max_args = rest ? Float::INFINITY : required.size + optional.size
243
+ return 0 unless arg_types.size.between?(min_args, max_args)
244
+
245
+ # Score each argument match
246
+ score = 0
247
+ arg_types.each_with_index do |arg_type, i|
248
+ param = if i < required.size
249
+ required[i]
250
+ elsif i < required.size + optional.size
251
+ optional[i - required.size]
252
+ else
253
+ rest
254
+ end
255
+
256
+ break unless param
257
+
258
+ score += type_match_score(arg_type, param.type)
259
+ end
260
+
261
+ score
262
+ end
263
+
264
+ # Calculate match score between our type and RBS parameter type
265
+ # @param our_type [Types::Type] our type representation
266
+ # @param rbs_type [RBS::Types::t] RBS type
267
+ # @return [Integer] score (0 = no match, 1 = weak match, 2 = exact match)
268
+ def type_match_score(our_type, rbs_type)
269
+ case rbs_type
270
+ when RBS::Types::ClassInstance
271
+ # Exact class match
272
+ class_name = rbs_type.name.to_s.delete_prefix("::")
273
+ return 2 if types_match_class?(our_type, class_name)
274
+
275
+ 0
276
+ when RBS::Types::Union
277
+ # Check if our type matches any member
278
+ max_score = rbs_type.types.map { |t| type_match_score(our_type, t) }.max || 0
279
+ max_score.positive? ? 1 : 0
280
+ else
281
+ # Unknown RBS type - give weak match to avoid penalizing
282
+ 1
283
+ end
284
+ end
285
+
286
+ # Check if our type matches a class name
287
+ # @param our_type [Types::Type] our type
288
+ # @param class_name [String] class name to match
289
+ # @return [Boolean] true if types match
290
+ def types_match_class?(our_type, class_name)
291
+ case our_type
292
+ when Types::ClassInstance
293
+ our_type.name == class_name
294
+ when Types::ArrayType
295
+ class_name == "Array"
296
+ when Types::HashShape
297
+ class_name == "Hash"
298
+ else
299
+ false
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Registry
6
+ # Stores and retrieves project method definitions
7
+ # Supports inheritance chain traversal when ancestry_provider is set
8
+ class MethodRegistry
9
+ # Callback for getting class ancestors
10
+ # @return [Proc, nil] A proc that takes class_name and returns array of ancestor names
11
+ attr_accessor :ancestry_provider
12
+
13
+ # @param ancestry_provider [Proc, nil] Returns ancestors for inheritance lookup
14
+ def initialize(ancestry_provider: nil)
15
+ @methods = {} # { "ClassName" => { "method_name" => DefNode } }
16
+ @ancestry_provider = ancestry_provider
17
+ end
18
+
19
+ # Register a method definition
20
+ # @param class_name [String] Class name (empty string for top-level)
21
+ # @param method_name [String] Method name
22
+ # @param def_node [IR::DefNode] Method definition node
23
+ def register(class_name, method_name, def_node)
24
+ @methods[class_name] ||= {}
25
+ @methods[class_name][method_name] = def_node
26
+ end
27
+
28
+ # Look up a method definition (with inheritance chain traversal)
29
+ # @param class_name [String] Class name
30
+ # @param method_name [String] Method name
31
+ # @return [IR::DefNode, nil] Method definition node or nil
32
+ def lookup(class_name, method_name)
33
+ # Try current class first
34
+ result = @methods.dig(class_name, method_name)
35
+ return result if result
36
+
37
+ # Traverse ancestor chain if provider available
38
+ return nil unless @ancestry_provider
39
+
40
+ ancestors = @ancestry_provider.call(class_name)
41
+ ancestors.each do |ancestor_name|
42
+ next if ancestor_name == class_name # Skip self
43
+
44
+ result = @methods.dig(ancestor_name, method_name)
45
+ return result if result
46
+ end
47
+
48
+ nil
49
+ end
50
+
51
+ # Get all registered class names
52
+ # @return [Array<String>] List of class names (frozen)
53
+ def registered_classes
54
+ @methods.keys.freeze
55
+ end
56
+
57
+ # Get all methods for a specific class (direct methods only)
58
+ # @param class_name [String] Class name
59
+ # @return [Hash<String, IR::DefNode>] Methods hash (frozen)
60
+ def methods_for_class(class_name)
61
+ (@methods[class_name] || {}).freeze
62
+ end
63
+
64
+ # Search for methods matching a pattern
65
+ # @param pattern [String] Search pattern (partial match on "ClassName#method_name")
66
+ # @return [Array<Array>] Array of [class_name, method_name, def_node]
67
+ def search(pattern)
68
+ results = []
69
+ @methods.each do |class_name, methods|
70
+ methods.each do |method_name, def_node|
71
+ full_name = "#{class_name}##{method_name}"
72
+ results << [class_name, method_name, def_node] if full_name.include?(pattern)
73
+ end
74
+ end
75
+ results
76
+ end
77
+
78
+ # Get all methods available on a class (including inherited)
79
+ # @param class_name [String]
80
+ # @return [Set<String>] Method names
81
+ def all_methods_for_class(class_name)
82
+ # Start with directly defined methods
83
+ class_methods = (@methods[class_name]&.keys || []).to_set
84
+
85
+ # Add inherited methods if ancestry_provider is available
86
+ return class_methods unless @ancestry_provider
87
+
88
+ ancestors = @ancestry_provider.call(class_name)
89
+ ancestors.each do |ancestor_name|
90
+ next if ancestor_name == class_name # Skip self
91
+
92
+ ancestor_methods = @methods[ancestor_name]&.keys || []
93
+ class_methods.merge(ancestor_methods)
94
+ end
95
+
96
+ class_methods
97
+ end
98
+
99
+ # Clear all registered methods
100
+ def clear
101
+ @methods.clear
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Registry
6
+ # Stores and retrieves instance/class variable write nodes
7
+ # Supports inheritance chain traversal for instance variables when ancestry_provider is set
8
+ class VariableRegistry
9
+ # Callback for getting class ancestors
10
+ # @return [Proc, nil] A proc that takes class_name and returns array of ancestor names
11
+ attr_accessor :ancestry_provider
12
+
13
+ # @param ancestry_provider [Proc, nil] Returns ancestors for inheritance lookup
14
+ def initialize(ancestry_provider: nil)
15
+ @instance_variables = {} # { "ClassName" => { :@name => WriteNode } }
16
+ @class_variables = {} # { "ClassName" => { :@@name => WriteNode } }
17
+ @ancestry_provider = ancestry_provider
18
+ end
19
+
20
+ # Register an instance variable write
21
+ # @param class_name [String] Class name
22
+ # @param name [Symbol] Variable name (e.g., :@recipe)
23
+ # @param write_node [IR::InstanceVariableWriteNode]
24
+ def register_instance_variable(class_name, name, write_node)
25
+ return unless class_name
26
+
27
+ @instance_variables[class_name] ||= {}
28
+ # First write wins (preserves consistent behavior)
29
+ @instance_variables[class_name][name] ||= write_node
30
+ end
31
+
32
+ # Look up an instance variable write (with inheritance chain traversal)
33
+ # @param class_name [String]
34
+ # @param name [Symbol]
35
+ # @return [IR::InstanceVariableWriteNode, nil]
36
+ def lookup_instance_variable(class_name, name)
37
+ return nil unless class_name
38
+
39
+ # Try current class first
40
+ result = @instance_variables.dig(class_name, name)
41
+ return result if result
42
+
43
+ # Traverse ancestor chain if provider available
44
+ return nil unless @ancestry_provider
45
+
46
+ ancestors = @ancestry_provider.call(class_name)
47
+ ancestors.each do |ancestor_name|
48
+ next if ancestor_name == class_name # Skip self
49
+
50
+ result = @instance_variables.dig(ancestor_name, name)
51
+ return result if result
52
+ end
53
+
54
+ nil
55
+ end
56
+
57
+ # Register a class variable write
58
+ # @param class_name [String]
59
+ # @param name [Symbol] (e.g., :@@count)
60
+ # @param write_node [IR::ClassVariableWriteNode]
61
+ def register_class_variable(class_name, name, write_node)
62
+ return unless class_name
63
+
64
+ @class_variables[class_name] ||= {}
65
+ # First write wins
66
+ @class_variables[class_name][name] ||= write_node
67
+ end
68
+
69
+ # Look up a class variable write
70
+ # @param class_name [String]
71
+ # @param name [Symbol]
72
+ # @return [IR::ClassVariableWriteNode, nil]
73
+ def lookup_class_variable(class_name, name)
74
+ return nil unless class_name
75
+
76
+ @class_variables.dig(class_name, name)
77
+ end
78
+
79
+ # Clear all registered variables
80
+ def clear
81
+ @instance_variables.clear
82
+ @class_variables.clear
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "types"
4
+
5
+ module TypeGuessr
6
+ module Core
7
+ # SignatureProvider aggregates multiple type sources for method signature lookups
8
+ # Uses priority-based resolution: first non-Unknown result wins
9
+ #
10
+ # @example
11
+ # provider = SignatureProvider.new
12
+ # provider.add_provider(ProjectRBSProvider.new, priority: :high)
13
+ # provider.add_provider(RBSProvider.instance)
14
+ #
15
+ # return_type = provider.get_method_return_type("String", "upcase")
16
+ class SignatureProvider
17
+ # @param providers [Array<#get_method_return_type>] Providers in priority order (first = highest)
18
+ def initialize(providers = [])
19
+ @providers = providers.dup
20
+ end
21
+
22
+ # Add a provider to the chain
23
+ # @param provider [#get_method_return_type] Provider implementing the signature protocol
24
+ # @param priority [:high, :low] Priority level (:high = first, :low = last)
25
+ def add_provider(provider, priority: :low)
26
+ case priority
27
+ when :high
28
+ @providers.unshift(provider)
29
+ when :low
30
+ @providers.push(provider)
31
+ end
32
+ end
33
+
34
+ # Get instance method return type with overload resolution
35
+ # @param class_name [String] Class name (e.g., "String", "Array")
36
+ # @param method_name [String] Method name (e.g., "upcase", "map")
37
+ # @param arg_types [Array<Types::Type>] Argument types for overload matching
38
+ # @return [Types::Type] Return type (Unknown if not found in any provider)
39
+ def get_method_return_type(class_name, method_name, arg_types = [])
40
+ @providers.each do |provider|
41
+ result = provider.get_method_return_type(class_name, method_name, arg_types)
42
+ return result unless result.is_a?(Types::Unknown)
43
+ end
44
+ Types::Unknown.instance
45
+ end
46
+
47
+ # Get class method return type (e.g., File.read, Array.new)
48
+ # @param class_name [String] Class name
49
+ # @param method_name [String] Method name
50
+ # @param arg_types [Array<Types::Type>] Argument types for overload matching
51
+ # @return [Types::Type] Return type (Unknown if not found in any provider)
52
+ def get_class_method_return_type(class_name, method_name, arg_types = [])
53
+ @providers.each do |provider|
54
+ result = provider.get_class_method_return_type(class_name, method_name, arg_types)
55
+ return result unless result.is_a?(Types::Unknown)
56
+ end
57
+ Types::Unknown.instance
58
+ end
59
+
60
+ # Get block parameter types for a method
61
+ # @param class_name [String] Class name
62
+ # @param method_name [String] Method name
63
+ # @return [Array<Types::Type>] Block parameter types (empty if no block or not found)
64
+ def get_block_param_types(class_name, method_name)
65
+ @providers.each do |provider|
66
+ result = provider.get_block_param_types(class_name, method_name)
67
+ return result unless result.empty?
68
+ end
69
+ []
70
+ end
71
+
72
+ # Get method signatures for hover display
73
+ # @param class_name [String] Class name
74
+ # @param method_name [String] Method name
75
+ # @return [Array<Signature>] Method signatures (empty if not found)
76
+ def get_method_signatures(class_name, method_name)
77
+ @providers.each do |provider|
78
+ next unless provider.respond_to?(:get_method_signatures)
79
+
80
+ result = provider.get_method_signatures(class_name, method_name)
81
+ return result unless result.empty?
82
+ end
83
+ []
84
+ end
85
+
86
+ # Get class method signatures for hover display (e.g., File.exist?, Dir.pwd)
87
+ # @param class_name [String] Class name
88
+ # @param method_name [String] Method name
89
+ # @return [Array<Signature>] Method signatures (empty if not found)
90
+ def get_class_method_signatures(class_name, method_name)
91
+ @providers.each do |provider|
92
+ next unless provider.respond_to?(:get_class_method_signatures)
93
+
94
+ result = provider.get_class_method_signatures(class_name, method_name)
95
+ return result unless result.empty?
96
+ end
97
+ []
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "types"
4
+
5
+ module TypeGuessr
6
+ module Core
7
+ # Simplifies types by unwrapping single-element unions and
8
+ # unifying parent/child class relationships
9
+ class TypeSimplifier
10
+ # @param ancestry_provider [Proc, nil] A proc that takes class_name and returns array of ancestor names
11
+ def initialize(ancestry_provider: nil)
12
+ @ancestry_provider = ancestry_provider
13
+ end
14
+
15
+ # Simplify a type
16
+ # @param type [Types::Type] The type to simplify
17
+ # @return [Types::Type] The simplified type
18
+ def simplify(type)
19
+ case type
20
+ when Types::Union
21
+ simplify_union(type)
22
+ else
23
+ type
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def simplify_union(union)
30
+ types = union.types
31
+
32
+ # 1. Single element: unwrap
33
+ return types.first if types.size == 1
34
+
35
+ # 2. Filter to most general types (remove children when parent is present)
36
+ types = filter_to_most_general_types(types) if @ancestry_provider
37
+
38
+ # 3. Check again after filtering
39
+ return types.first if types.size == 1
40
+
41
+ # 4. Multiple elements remain: create new Union
42
+ Types::Union.new(types)
43
+ end
44
+
45
+ # Filter out types whose ancestor is also in the list
46
+ # @param types [Array<Types::Type>] List of types
47
+ # @return [Array<Types::Type>] Filtered list with only the most general types
48
+ def filter_to_most_general_types(types)
49
+ # Extract class names from ClassInstance types
50
+ class_names = types.filter_map do |t|
51
+ t.name if t.is_a?(Types::ClassInstance)
52
+ end
53
+
54
+ types.reject do |type|
55
+ next false unless type.is_a?(Types::ClassInstance)
56
+
57
+ ancestors = @ancestry_provider.call(type.name)
58
+ # Check if any ancestor (excluding self) is also in the list
59
+ ancestors.any? { |ancestor| ancestor != type.name && class_names.include?(ancestor) }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end