holistic-ruby 0.1.4 → 0.1.7
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +5 -5
- data/exe/holistic-ruby +0 -1
- data/lib/holistic/application.rb +12 -4
- data/lib/holistic/database/migrations.rb +23 -0
- data/lib/holistic/database/node.rb +37 -0
- data/lib/holistic/database/relation.rb +21 -0
- data/lib/holistic/database.rb +57 -0
- data/lib/holistic/document/file/record.rb +10 -0
- data/lib/holistic/document/file/repository.rb +24 -0
- data/lib/holistic/document/file/store.rb +13 -0
- data/lib/holistic/document/location.rb +4 -6
- data/lib/holistic/document/unsaved/record.rb +12 -3
- data/lib/holistic/extensions/ruby/stdlib.rb +8 -8
- data/lib/holistic/language_server/requests/lifecycle/initialize.rb +5 -10
- data/lib/holistic/language_server/requests/text_document/completion.rb +16 -5
- data/lib/holistic/language_server/requests/text_document/did_close.rb +5 -9
- data/lib/holistic/language_server/requests/text_document/did_open.rb +1 -0
- data/lib/holistic/language_server/requests/text_document/did_save.rb +5 -9
- data/lib/holistic/language_server/requests/text_document/find_references.rb +7 -4
- data/lib/holistic/language_server/requests/text_document/go_to_definition.rb +3 -2
- data/lib/holistic/language_server/stdio/parser.rb +2 -2
- data/lib/holistic/language_server/stdio/start.rb +1 -1
- data/lib/holistic/ruby/autocompletion/suggest.rb +45 -25
- data/lib/holistic/ruby/parser/constant_resolution.rb +11 -11
- data/lib/holistic/ruby/parser/live_editing/process_file_changed.rb +23 -23
- data/lib/holistic/ruby/parser/program_visitor.rb +62 -29
- data/lib/holistic/ruby/parser.rb +51 -11
- data/lib/holistic/ruby/reference/delete.rb +18 -0
- data/lib/holistic/ruby/reference/find_referenced_scope.rb +2 -2
- data/lib/holistic/ruby/reference/record.rb +15 -8
- data/lib/holistic/ruby/reference/repository.rb +19 -41
- data/lib/holistic/ruby/reference/store.rb +29 -0
- data/lib/holistic/ruby/scope/delete.rb +29 -0
- data/lib/holistic/ruby/scope/lexical.rb +11 -0
- data/lib/holistic/ruby/scope/list_class_methods.rb +19 -0
- data/lib/holistic/ruby/scope/list_instance_methods.rb +19 -0
- data/lib/holistic/ruby/scope/list_references.rb +3 -3
- data/lib/holistic/ruby/scope/location.rb +9 -9
- data/lib/holistic/ruby/scope/outline.rb +10 -10
- data/lib/holistic/ruby/scope/record.rb +20 -50
- data/lib/holistic/ruby/scope/repository.rb +29 -26
- data/lib/holistic/ruby/scope/store.rb +45 -0
- data/lib/holistic/ruby/type_inference/clue/method_call.rb +1 -0
- data/lib/holistic/ruby/type_inference/clue/reference_to_superclass.rb +9 -0
- data/lib/holistic/ruby/type_inference/clue/scope_reference.rb +1 -0
- data/lib/holistic/ruby/type_inference/processing_queue.rb +28 -0
- data/lib/holistic/ruby/type_inference/resolver/class_method.rb +9 -0
- data/lib/holistic/ruby/type_inference/resolver/instance_method.rb +9 -0
- data/lib/holistic/ruby/type_inference/resolver/scope.rb +24 -0
- data/lib/holistic/ruby/type_inference/solve.rb +25 -69
- data/lib/holistic/ruby/type_inference/solve_pending_references.rb +3 -1
- data/lib/holistic/version.rb +1 -1
- metadata +21 -10
- data/lib/holistic/database/table.rb +0 -78
- data/lib/holistic/document/file.rb +0 -36
- data/lib/holistic/ruby/parser/table_of_contents.rb +0 -17
- data/lib/holistic/ruby/reference/register.rb +0 -15
- data/lib/holistic/ruby/reference/unregister.rb +0 -11
- data/lib/holistic/ruby/scope/register.rb +0 -31
- data/lib/holistic/ruby/scope/unregister.rb +0 -27
- data/lib/holistic/ruby/type_inference/conclusion.rb +0 -20
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Reference
|
4
|
+
module Store
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, processing_queue:, scope:, clues:, location:)
|
8
|
+
record = Record.new(location.identifier, { identifier: location.identifier, clues:, location: })
|
9
|
+
|
10
|
+
reference = database.store(location.identifier, record)
|
11
|
+
|
12
|
+
reference.relation(:located_in_scope).add!(scope)
|
13
|
+
reference.relation(:reference_defined_in_file).add!(location.file)
|
14
|
+
|
15
|
+
# resolving reference to superclasses need to happen before resolving reference to methods because the
|
16
|
+
# relation ancestor-descentand needs to exist beforehand.
|
17
|
+
# in other words, if we try to resolve a reference to a method *before* resolving the superclass
|
18
|
+
# we might get a miss.
|
19
|
+
should_resolve_type_inference_with_high_priority =
|
20
|
+
reference.find_clue(::Holistic::Ruby::TypeInference::Clue::ReferenceToSuperclass).present?
|
21
|
+
|
22
|
+
if should_resolve_type_inference_with_high_priority
|
23
|
+
processing_queue.push_with_high_priority(reference)
|
24
|
+
else
|
25
|
+
processing_queue.push(reference)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope
|
4
|
+
module Delete
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, fully_qualified_name:, file_path:)
|
8
|
+
scope = database.find(fully_qualified_name)
|
9
|
+
|
10
|
+
return :scope_not_found if scope.nil?
|
11
|
+
|
12
|
+
location_to_remove = scope.locations.find { |scope_location| scope_location.declaration.file.path == file_path }
|
13
|
+
|
14
|
+
return :scope_not_defined_in_speciefied_file if location_to_remove.nil?
|
15
|
+
|
16
|
+
scope.locations.delete(location_to_remove)
|
17
|
+
|
18
|
+
scope.relation(:scope_defined_in_file).delete!(location_to_remove.declaration.file)
|
19
|
+
|
20
|
+
if scope.locations.empty?
|
21
|
+
scope.relation(:lexical_parent).delete!(scope.lexical_parent)
|
22
|
+
|
23
|
+
database.delete(fully_qualified_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
:definition_unregistered
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope::Lexical
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def descendant?(child:, parent:)
|
7
|
+
child_parent = child.lexical_parent
|
8
|
+
|
9
|
+
child_parent.present? && (child_parent == parent || descendant?(child: child_parent, parent:))
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope::ListClassMethods
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def call(scope:)
|
7
|
+
class_methods = scope.lexical_children.filter(&:class_method?)
|
8
|
+
class_method_names = ::Set.new(class_methods.map(&:name))
|
9
|
+
|
10
|
+
ancestor_methods = scope.ancestors.flat_map do |ancestor|
|
11
|
+
ancestor_methods = call(scope: ancestor)
|
12
|
+
|
13
|
+
# reject parent methods that were overriden by the subclass
|
14
|
+
ancestor_methods.reject { |method| class_method_names.include?(method.name) }
|
15
|
+
end
|
16
|
+
|
17
|
+
class_methods + ancestor_methods
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope::ListInstanceMethods
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def call(scope:)
|
7
|
+
instance_methods = scope.lexical_children.filter(&:instance_method?)
|
8
|
+
instance_method_names = ::Set.new(instance_methods.map(&:name))
|
9
|
+
|
10
|
+
ancestor_methods = scope.ancestors.flat_map do |ancestor|
|
11
|
+
ancestor_methods = call(scope: ancestor)
|
12
|
+
|
13
|
+
# reject parent methods that were overriden by the subclass
|
14
|
+
ancestor_methods.reject { |method| instance_method_names.include?(method.name) }
|
15
|
+
end
|
16
|
+
|
17
|
+
instance_methods + ancestor_methods
|
18
|
+
end
|
19
|
+
end
|
@@ -5,16 +5,16 @@ module Holistic::Ruby::Scope
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
QueryReferencesRecursively = ->(application, scope) do
|
8
|
-
references_to_scope =
|
8
|
+
references_to_scope = scope.referenced_by.to_a
|
9
9
|
|
10
|
-
references_to_child_scopes = scope.
|
10
|
+
references_to_child_scopes = scope.lexical_children.flat_map { QueryReferencesRecursively.call(application, _1) }
|
11
11
|
|
12
12
|
references_to_scope + references_to_child_scopes
|
13
13
|
end
|
14
14
|
|
15
15
|
Relevance = ->(reference) do
|
16
16
|
# TODO: should the location answer the kind of file it is? application code, config, spec, etc. Not sure.
|
17
|
-
looks_like_a_spec = reference.location.
|
17
|
+
looks_like_a_spec = reference.location.file.path.include?("_spec.rb") || reference.location.file.path.include?("_test.rb")
|
18
18
|
|
19
19
|
looks_like_a_spec ? 1 : 0
|
20
20
|
end
|
@@ -3,13 +3,11 @@
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
4
|
class Location
|
5
5
|
class Collection
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :scope_name, :items
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
11
|
-
@scope = scope
|
12
|
-
@items = location.nil? ? [] : [location]
|
8
|
+
def initialize(scope_name)
|
9
|
+
@scope_name = scope_name
|
10
|
+
@items = []
|
13
11
|
end
|
14
12
|
|
15
13
|
def main
|
@@ -18,17 +16,19 @@ module Holistic::Ruby::Scope
|
|
18
16
|
|
19
17
|
delegate :<<, to: :items
|
20
18
|
delegate :each, to: :items
|
19
|
+
delegate :find, to: :items
|
21
20
|
delegate :map, to: :items
|
22
|
-
delegate :
|
21
|
+
delegate :delete, to: :items
|
23
22
|
delegate :any?, to: :items
|
23
|
+
delegate :empty?, to: :items
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def location_matching_scope_name
|
28
|
-
scope_name_in_snake_case =
|
28
|
+
scope_name_in_snake_case = scope_name.underscore
|
29
29
|
|
30
30
|
items.find do |location|
|
31
|
-
::File.basename(location.declaration.
|
31
|
+
::File.basename(location.declaration.file.path) == "#{scope_name_in_snake_case}.rb"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -13,38 +13,38 @@ module Holistic::Ruby::Scope
|
|
13
13
|
)
|
14
14
|
|
15
15
|
QueryChildScopesRecursively = ->(application, scope) do
|
16
|
-
scope.
|
16
|
+
scope.lexical_children.to_a + scope.lexical_children.flat_map { QueryChildScopesRecursively[application, _1] }
|
17
17
|
end
|
18
18
|
|
19
19
|
QueryDependenciesRecursively = ->(application, outlined_scope, scope) do
|
20
20
|
is_local_dependency = ->(reference) do
|
21
|
-
scope =
|
21
|
+
scope = reference.referenced_scope
|
22
22
|
|
23
|
-
scope
|
23
|
+
scope == outlined_scope || Lexical.descendant?(child: scope, parent: outlined_scope)
|
24
24
|
end
|
25
25
|
|
26
26
|
dependencies = []
|
27
27
|
|
28
28
|
scope.locations.each do |scope_location|
|
29
29
|
application.references
|
30
|
-
.list_references_in_file(scope_location.declaration.
|
31
|
-
.filter { |reference| reference.
|
32
|
-
.filter { |reference| reference.
|
30
|
+
.list_references_in_file(scope_location.declaration.file.path)
|
31
|
+
.filter { |reference| reference.located_in_scope == scope }
|
32
|
+
.filter { |reference| reference.referenced_scope.present? }
|
33
33
|
.reject(&is_local_dependency)
|
34
34
|
.tap { dependencies.concat(_1) }
|
35
35
|
end
|
36
36
|
|
37
|
-
scope.
|
37
|
+
scope.lexical_children.map(&QueryDependenciesRecursively.curry[application, outlined_scope]).flatten.concat(dependencies)
|
38
38
|
end
|
39
39
|
|
40
40
|
def call(application:, scope:)
|
41
41
|
declarations = QueryChildScopesRecursively.call(application, scope).sort_by { _1.fully_qualified_name }
|
42
42
|
|
43
|
-
dependencies = QueryDependenciesRecursively.call(application, scope, scope).uniq
|
43
|
+
dependencies = QueryDependenciesRecursively.call(application, scope, scope).uniq
|
44
44
|
|
45
|
-
references =
|
45
|
+
references = scope.referenced_by
|
46
46
|
|
47
|
-
dependants = references.map { |reference| reference.
|
47
|
+
dependants = references.map { |reference| reference.located_in_scope }.uniq
|
48
48
|
|
49
49
|
Result.new(declarations:, dependencies:, references:, dependants:)
|
50
50
|
end
|
@@ -1,56 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
|
-
class Record
|
5
|
-
|
6
|
-
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
"#{parent.fully_qualified_name}#{separator}#{name}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def root?
|
29
|
-
parent.nil?
|
30
|
-
end
|
31
|
-
|
32
|
-
def lambda?
|
33
|
-
kind == Kind::LAMBDA
|
34
|
-
end
|
35
|
-
|
36
|
-
def class?
|
37
|
-
kind == Kind::CLASS
|
38
|
-
end
|
39
|
-
|
40
|
-
def module?
|
41
|
-
kind == Kind::MODULE
|
42
|
-
end
|
43
|
-
|
44
|
-
def instance_method?
|
45
|
-
kind == Kind::INSTANCE_METHOD
|
46
|
-
end
|
47
|
-
|
48
|
-
def class_method?
|
49
|
-
kind == Kind::CLASS_METHOD
|
50
|
-
end
|
51
|
-
|
52
|
-
def descendant?(other)
|
53
|
-
parent.present? && (parent == other || parent.descendant?(other))
|
4
|
+
class Record < ::Holistic::Database::Node
|
5
|
+
def fully_qualified_name = attr(:fully_qualified_name)
|
6
|
+
def locations = attr(:locations)
|
7
|
+
def name = attr(:name)
|
8
|
+
def kind = attr(:kind)
|
9
|
+
|
10
|
+
def lexical_parent = has_one(:lexical_parent)
|
11
|
+
def lexical_children = has_many(:lexical_children)
|
12
|
+
def ancestors = has_many(:ancestors)
|
13
|
+
def descendants = has_many(:descendants)
|
14
|
+
def referenced_by = has_many(:referenced_by)
|
15
|
+
|
16
|
+
def root? = kind == Kind::ROOT
|
17
|
+
def class? = kind == Kind::CLASS
|
18
|
+
def class_method? = kind == Kind::CLASS_METHOD
|
19
|
+
def instance_method? = kind == Kind::INSTANCE_METHOD
|
20
|
+
def method? = class_method? || instance_method?
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"<#{self.class.name} kind=#{kind} fully_qualified_name=#{fully_qualified_name}>"
|
54
24
|
end
|
55
25
|
end
|
56
26
|
end
|
@@ -2,48 +2,51 @@
|
|
2
2
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
4
|
class Repository
|
5
|
-
attr_reader :
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
attr_reader :database, :root
|
6
|
+
|
7
|
+
def initialize(database:)
|
8
|
+
@root = database.store(
|
9
|
+
"root_scope",
|
10
|
+
Record.new("root_scope", {
|
11
|
+
fully_qualified_name: "::",
|
12
|
+
name: "::",
|
13
|
+
kind: Kind::ROOT
|
14
|
+
})
|
15
|
+
)
|
16
|
+
|
17
|
+
@database = database
|
17
18
|
end
|
18
19
|
|
19
|
-
def
|
20
|
-
|
20
|
+
def find(fully_qualified_name)
|
21
|
+
database.find(fully_qualified_name)
|
21
22
|
end
|
22
23
|
|
23
24
|
def find_by_cursor(cursor)
|
24
|
-
|
25
|
-
|
25
|
+
database.find(cursor.file_path)&.then do |file|
|
26
|
+
file.defines_scopes.find do |scope|
|
27
|
+
scope.locations.any? { _1.declaration.contains?(cursor) }
|
28
|
+
end
|
26
29
|
end
|
27
|
-
|
28
|
-
nil
|
29
30
|
end
|
30
31
|
|
31
32
|
def find_inner_most_scope_by_cursor(cursor)
|
32
|
-
|
33
|
+
file = database.find(cursor.file_path)
|
34
|
+
|
35
|
+
return nil if file.nil?
|
33
36
|
|
34
|
-
matching_scopes =
|
35
|
-
scope.locations.
|
37
|
+
matching_scopes = file.defines_scopes.filter_map do |scope|
|
38
|
+
scope.locations.find { |location| location.body.contains?(cursor) }&.then do |location|
|
39
|
+
{ location:, scope: }
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
|
-
matching_scopes.last
|
39
|
-
end
|
43
|
+
inner_most_matching_scope = matching_scopes.sort_by { |match| match[:location].declaration.start_line }.last
|
40
44
|
|
41
|
-
|
42
|
-
table.delete(fully_qualified_name)
|
45
|
+
inner_most_matching_scope&.then { _1[:scope] }
|
43
46
|
end
|
44
47
|
|
45
48
|
def list_scopes_in_file(file_path)
|
46
|
-
|
49
|
+
database.find(file_path)&.defines_scopes || []
|
47
50
|
end
|
48
51
|
end
|
49
52
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope
|
4
|
+
module Store
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, lexical_parent:, kind:, name:, location:)
|
8
|
+
fully_qualified_name = build_fully_qualified_name(lexical_parent:, kind:, name:)
|
9
|
+
|
10
|
+
scope = database.find(fully_qualified_name)
|
11
|
+
|
12
|
+
if scope.nil?
|
13
|
+
record = Record.new(fully_qualified_name, { fully_qualified_name:, name:, kind:, locations: Location::Collection.new(name) })
|
14
|
+
|
15
|
+
scope = database.store(fully_qualified_name, record)
|
16
|
+
end
|
17
|
+
|
18
|
+
scope.locations << location
|
19
|
+
|
20
|
+
scope.relation(:lexical_parent).add!(lexical_parent)
|
21
|
+
scope.relation(:scope_defined_in_file).add!(location.declaration.file)
|
22
|
+
|
23
|
+
scope
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_fully_qualified_name(lexical_parent:, kind:, name:)
|
29
|
+
parent_fully_qualified_name =
|
30
|
+
case lexical_parent.kind
|
31
|
+
when Kind::ROOT then ""
|
32
|
+
else lexical_parent.fully_qualified_name
|
33
|
+
end
|
34
|
+
|
35
|
+
separator =
|
36
|
+
case kind
|
37
|
+
when Kind::INSTANCE_METHOD then "#"
|
38
|
+
when Kind::CLASS_METHOD then "."
|
39
|
+
else "::"
|
40
|
+
end
|
41
|
+
|
42
|
+
"#{parent_fully_qualified_name}#{separator}#{name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::TypeInference
|
4
|
+
class ProcessingQueue
|
5
|
+
def initialize
|
6
|
+
@high_priority_queue = ::Queue.new
|
7
|
+
@queue = ::Queue.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def push(item)
|
11
|
+
@queue.push(item)
|
12
|
+
end
|
13
|
+
|
14
|
+
def push_with_high_priority(item)
|
15
|
+
@high_priority_queue.push(item)
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
@high_priority_queue.empty? && @queue.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def pop
|
23
|
+
return @high_priority_queue.pop if @high_priority_queue.size > 0
|
24
|
+
|
25
|
+
@queue.pop
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::TypeInference::Resolver::Scope
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def resolve(application:, nesting:, resolution_possibilities:)
|
7
|
+
resolution_possibilities = ["::"] if nesting.root_scope_resolution?
|
8
|
+
|
9
|
+
resolution_possibilities.each do |resolution_candidate|
|
10
|
+
fully_qualified_scope_name =
|
11
|
+
if resolution_candidate == "::"
|
12
|
+
"::#{nesting.to_s}"
|
13
|
+
else
|
14
|
+
"#{resolution_candidate}::#{nesting.to_s}"
|
15
|
+
end
|
16
|
+
|
17
|
+
scope = application.scopes.find(fully_qualified_scope_name)
|
18
|
+
|
19
|
+
return scope if scope.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
@@ -5,52 +5,47 @@ module Holistic::Ruby::TypeInference
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
def call(application:, reference:)
|
8
|
-
|
8
|
+
referenced_scope =
|
9
9
|
solve_scope_reference(application:, reference:) ||
|
10
|
-
solve_method_call(application:, reference:)
|
11
|
-
Conclusion.unresolved
|
10
|
+
solve_method_call(application:, reference:)
|
12
11
|
|
13
|
-
|
12
|
+
if referenced_scope
|
13
|
+
reference.relation(:referenced_scope).add!(referenced_scope)
|
14
|
+
|
15
|
+
# NOTE: should this be an event that is handled by stdlib? I guess inheritance support with dedicated syntax
|
16
|
+
# is part of the language core, so it makes sense being here. Let me think about this for a bit.
|
17
|
+
reference.find_clue(Clue::ReferenceToSuperclass)&.then do |reference_to_superclass_clue|
|
18
|
+
referenced_scope.relation(:descendants).add!(reference_to_superclass_clue.subclass_scope)
|
19
|
+
end
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
private
|
17
24
|
|
18
25
|
def solve_scope_reference(application:, reference:)
|
19
|
-
|
20
|
-
reference.clues.one? && reference.clues.first.is_a?(Clue::ScopeReference)
|
21
|
-
|
22
|
-
return unless has_scope_reference_clue
|
26
|
+
reference_to_scope_clue = reference.find_clue(Clue::ScopeReference)
|
23
27
|
|
24
|
-
|
28
|
+
return if reference_to_scope_clue.nil?
|
25
29
|
|
26
|
-
|
30
|
+
Resolver::Scope.resolve(
|
27
31
|
application:,
|
28
|
-
nesting:
|
29
|
-
resolution_possibilities:
|
32
|
+
nesting: reference_to_scope_clue.nesting,
|
33
|
+
resolution_possibilities: reference_to_scope_clue.resolution_possibilities
|
30
34
|
)
|
31
|
-
|
32
|
-
if referenced_scope.present?
|
33
|
-
return Conclusion.done(referenced_scope.fully_qualified_name)
|
34
|
-
end
|
35
|
-
|
36
|
-
nil
|
37
35
|
end
|
38
36
|
|
39
37
|
SolveMethodCallInCurrentScope = ->(application:, reference:, method_call_clue:) do
|
40
|
-
|
41
|
-
if reference.scope.class_method?
|
42
|
-
resolve_class_method(application:, scope: reference.scope.parent, method_name: method_call_clue.method_name)
|
43
|
-
elsif reference.scope.instance_method? && reference.scope.parent.present?
|
44
|
-
resolve_instance_method(application:, scope: reference.scope.parent, method_name: method_call_clue.method_name)
|
45
|
-
end
|
46
|
-
|
47
|
-
return if referenced_method.nil?
|
38
|
+
scope = reference.located_in_scope
|
48
39
|
|
49
|
-
|
40
|
+
if scope.class_method?
|
41
|
+
Resolver::ClassMethod.resolve(scope: scope.lexical_parent, method_name: method_call_clue.method_name)
|
42
|
+
elsif scope.instance_method? && scope.lexical_parent.present?
|
43
|
+
Resolver::InstanceMethod.resolve(scope: scope.lexical_parent, method_name: method_call_clue.method_name)
|
44
|
+
end
|
50
45
|
end
|
51
46
|
|
52
47
|
SolveMethodCallInSpecifiedScope = ->(application:, reference:, method_call_clue:) do
|
53
|
-
referenced_scope =
|
48
|
+
referenced_scope = Resolver::Scope.resolve(
|
54
49
|
application:,
|
55
50
|
nesting: method_call_clue.nesting,
|
56
51
|
resolution_possibilities: method_call_clue.resolution_possibilities
|
@@ -59,16 +54,8 @@ module Holistic::Ruby::TypeInference
|
|
59
54
|
return if referenced_scope.nil?
|
60
55
|
|
61
56
|
referenced_method = application.extensions.dispatch(:resolve_method_call_known_scope, { reference:, referenced_scope:, method_call_clue: })
|
62
|
-
referenced_method ||= resolve_class_method(application:, scope: referenced_scope, method_name: method_call_clue.method_name)
|
63
|
-
|
64
|
-
Conclusion.done(referenced_method.fully_qualified_name) if referenced_method.present?
|
65
|
-
end
|
66
|
-
|
67
|
-
SolveMethodCallInLocalVariable = ->(application:, reference:, method_call_clue:) do
|
68
|
-
# local_variable_name = method_call_clue.nesting.to_s
|
69
|
-
# referenced_scope = guess_scope_for_local_variable(scope: reference.scope, name: local_variable_name)
|
70
57
|
|
71
|
-
|
58
|
+
referenced_method || Resolver::ClassMethod.resolve(scope: referenced_scope, method_name: method_call_clue.method_name)
|
72
59
|
end
|
73
60
|
|
74
61
|
def solve_method_call(application:, reference:)
|
@@ -83,39 +70,8 @@ module Holistic::Ruby::TypeInference
|
|
83
70
|
elsif method_call_clue.nesting.constant?
|
84
71
|
SolveMethodCallInSpecifiedScope.call(application:, reference:, method_call_clue:)
|
85
72
|
else
|
86
|
-
|
73
|
+
nil # TODO
|
87
74
|
end
|
88
75
|
end
|
89
|
-
|
90
|
-
def resolve_scope(application:, nesting:, resolution_possibilities:)
|
91
|
-
resolution_possibilities = ["::"] if nesting.root_scope_resolution?
|
92
|
-
|
93
|
-
resolution_possibilities.each do |resolution_candidate|
|
94
|
-
fully_qualified_scope_name =
|
95
|
-
if resolution_candidate == "::"
|
96
|
-
"::#{nesting.to_s}"
|
97
|
-
else
|
98
|
-
"#{resolution_candidate}::#{nesting.to_s}"
|
99
|
-
end
|
100
|
-
|
101
|
-
scope = application.scopes.find_by_fully_qualified_name(fully_qualified_scope_name)
|
102
|
-
|
103
|
-
return scope if scope.present?
|
104
|
-
end
|
105
|
-
|
106
|
-
nil
|
107
|
-
end
|
108
|
-
|
109
|
-
def resolve_instance_method(application:, scope:, method_name:)
|
110
|
-
method_fully_qualified_name = "#{scope.fully_qualified_name}##{method_name}"
|
111
|
-
|
112
|
-
application.scopes.find_by_fully_qualified_name(method_fully_qualified_name)
|
113
|
-
end
|
114
|
-
|
115
|
-
def resolve_class_method(application:, scope:, method_name:)
|
116
|
-
method_fully_qualified_name = "#{scope.fully_qualified_name}.#{method_name}"
|
117
|
-
|
118
|
-
application.scopes.find_by_fully_qualified_name(method_fully_qualified_name)
|
119
|
-
end
|
120
76
|
end
|
121
77
|
end
|