yoda-language-server 0.8.0 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +22 -27
- data/client/vscode/README.md +17 -2
- data/client/vscode/package-lock.json +908 -817
- data/client/vscode/package.json +17 -7
- data/client/vscode/src/check-versions.ts +49 -0
- data/client/vscode/src/config.ts +8 -1
- data/client/vscode/src/extension.ts +4 -3
- data/client/vscode/src/install-tools.ts +56 -16
- data/client/vscode/src/language-server.ts +5 -0
- data/client/vscode/src/test/runTest.ts +1 -1
- data/client/vscode/src/test/suite/hover.test.ts +13 -25
- data/client/vscode/src/test/suite/index.ts +1 -2
- data/client/vscode/src/utils.ts +11 -0
- data/images/hover-method.png +0 -0
- data/images/method-complete.png +0 -0
- data/lib/yoda/cli/analyze_deps.rb +46 -25
- data/lib/yoda/id_mask.rb +84 -0
- data/lib/yoda/instrument.rb +7 -0
- data/lib/yoda/model/descriptions/require_path_description.rb +45 -0
- data/lib/yoda/model/descriptions.rb +1 -0
- data/lib/yoda/model/node_signatures/node.rb +9 -1
- data/lib/yoda/model/values/literal_value.rb +3 -0
- data/lib/yoda/parsing/location.rb +9 -0
- data/lib/yoda/server/concurrent_writer.rb +1 -1
- data/lib/yoda/server/lifecycle_handler.rb +101 -78
- data/lib/yoda/server/notifier.rb +2 -55
- data/lib/yoda/services/loadable_path_resolver.rb +40 -0
- data/lib/yoda/services.rb +1 -0
- data/lib/yoda/store/actions/read_project_files.rb +9 -7
- data/lib/yoda/store/adapters/gdbm_adapter/namespace_accessor.rb +1 -1
- data/lib/yoda/store/adapters/memory_adapter.rb +1 -1
- data/lib/yoda/store/objects/libraries_status.rb +1 -1
- data/lib/yoda/store/objects/library/core.rb +8 -0
- data/lib/yoda/store/objects/library/gem.rb +14 -3
- data/lib/yoda/store/objects/library/path_resolvable.rb +29 -0
- data/lib/yoda/store/objects/library/std.rb +9 -0
- data/lib/yoda/store/objects/library.rb +1 -0
- data/lib/yoda/store/objects/patch.rb +1 -1
- data/lib/yoda/store/objects/patch_set.rb +2 -2
- data/lib/yoda/store/project/dependency.rb +22 -4
- data/lib/yoda/store/project/file_finder.rb +20 -0
- data/lib/yoda/store/project.rb +2 -0
- data/lib/yoda/store/registry/cache.rb +2 -2
- data/lib/yoda/store/registry/composer.rb +9 -7
- data/lib/yoda/store/registry/index.rb +14 -10
- data/lib/yoda/store/registry/library_registry.rb +3 -1
- data/lib/yoda/store/registry.rb +1 -1
- data/lib/yoda/typing/constant_resolver/code_query.rb +25 -0
- data/lib/yoda/typing/constant_resolver/query.rb +12 -1
- data/lib/yoda/typing/constant_resolver.rb +13 -8
- data/lib/yoda/typing/inferencer/load_resolver.rb +37 -0
- data/lib/yoda/typing/inferencer/tracer.rb +32 -0
- data/lib/yoda/typing/inferencer.rb +3 -2
- data/lib/yoda/typing/node_info.rb +5 -0
- data/lib/yoda/typing/tree/{defined.rb → ask_defined.rb} +3 -2
- data/lib/yoda/typing/tree/base.rb +65 -20
- data/lib/yoda/typing/tree/begin.rb +5 -5
- data/lib/yoda/typing/tree/block_call.rb +26 -0
- data/lib/yoda/typing/tree/case.rb +8 -19
- data/lib/yoda/typing/tree/class_tree.rb +10 -18
- data/lib/yoda/typing/tree/conditional_loop.rb +15 -0
- data/lib/yoda/typing/tree/constant.rb +19 -0
- data/lib/yoda/typing/tree/constant_assignment.rb +2 -2
- data/lib/yoda/typing/tree/ensure.rb +17 -0
- data/lib/yoda/typing/tree/for.rb +7 -0
- data/lib/yoda/typing/tree/hash_tree.rb +32 -0
- data/lib/yoda/typing/tree/if.rb +10 -5
- data/lib/yoda/typing/tree/interpolation_text.rb +21 -0
- data/lib/yoda/typing/tree/literal.rb +8 -36
- data/lib/yoda/typing/tree/literal_inferable.rb +48 -0
- data/lib/yoda/typing/tree/local_exit.rb +15 -0
- data/lib/yoda/typing/tree/logical_assignment.rb +5 -5
- data/lib/yoda/typing/tree/logical_operator.rb +6 -5
- data/lib/yoda/typing/tree/method_def.rb +41 -0
- data/lib/yoda/typing/tree/method_inferable.rb +51 -0
- data/lib/yoda/typing/tree/module_tree.rb +7 -20
- data/lib/yoda/typing/tree/multiple_assignment.rb +6 -10
- data/lib/yoda/typing/tree/namespace_inferable.rb +20 -0
- data/lib/yoda/typing/tree/rescue.rb +18 -0
- data/lib/yoda/typing/tree/rescue_clause.rb +42 -0
- data/lib/yoda/typing/tree/self.rb +2 -1
- data/lib/yoda/typing/tree/send.rb +8 -60
- data/lib/yoda/typing/tree/send_inferable.rb +89 -0
- data/lib/yoda/typing/tree/singleton_class_tree.rb +24 -0
- data/lib/yoda/typing/tree/singleton_method_def.rb +41 -0
- data/lib/yoda/typing/tree/super.rb +9 -2
- data/lib/yoda/typing/tree/variable.rb +5 -10
- data/lib/yoda/typing/tree/variable_assignment.rb +11 -8
- data/lib/yoda/typing/tree/yield.rb +9 -2
- data/lib/yoda/typing/tree.rb +55 -22
- data/lib/yoda/typing.rb +1 -0
- data/lib/yoda/version.rb +1 -1
- data/lib/yoda.rb +1 -0
- data/yoda-language-server.gemspec +1 -1
- metadata +35 -18
- data/lib/yoda/typing/inferencer/ast_traverser.rb +0 -408
- data/lib/yoda/typing/tree/block.rb +0 -12
- data/lib/yoda/typing/tree/const.rb +0 -12
- data/lib/yoda/typing/tree/escape.rb +0 -12
- data/lib/yoda/typing/tree/hash_body.rb +0 -36
- data/lib/yoda/typing/tree/literal_with_interpolation.rb +0 -21
- data/lib/yoda/typing/tree/method.rb +0 -43
- data/lib/yoda/typing/tree/rescue_body.rb +0 -12
- data/lib/yoda/typing/tree/singleton_method.rb +0 -47
- data/lib/yoda/typing/tree/while.rb +0 -12
@@ -49,6 +49,16 @@ module Yoda
|
|
49
49
|
expand_path('.yoda.yml')
|
50
50
|
end
|
51
51
|
|
52
|
+
# @return [Array<String>]
|
53
|
+
def project_source_paths
|
54
|
+
glob(["{lib,app}/**/*.rb", "ext/**/*.c", ".yoda/*.rb"])
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<String>]
|
58
|
+
def project_load_paths
|
59
|
+
["lib", "app", "ext"].map { |path| expand_path(path) }.compact
|
60
|
+
end
|
61
|
+
|
52
62
|
# @return [String, nil]
|
53
63
|
def readable_config_file_path
|
54
64
|
path = config_file_path
|
@@ -95,6 +105,16 @@ module Yoda
|
|
95
105
|
def make_dir_at(dir_path)
|
96
106
|
dir_path && (File.exist?(dir_path) || FileUtils.mkdir_p(dir_path))
|
97
107
|
end
|
108
|
+
|
109
|
+
# @param pattern [String, Array<String>]
|
110
|
+
# @return [Array<String>]
|
111
|
+
def glob(pattern, base_path = project.root_path)
|
112
|
+
if project.root_path
|
113
|
+
Dir.glob(pattern, base: base_path).map { |path| File.expand_path(path, base_path) }
|
114
|
+
else
|
115
|
+
[]
|
116
|
+
end
|
117
|
+
end
|
98
118
|
end
|
99
119
|
end
|
100
120
|
end
|
data/lib/yoda/store/project.rb
CHANGED
@@ -9,20 +9,22 @@ module Yoda
|
|
9
9
|
|
10
10
|
def initialize(id:, registries: [])
|
11
11
|
@id = id
|
12
|
-
@registries = registries.map { |registry| [registry.id, registry] }.to_h
|
12
|
+
@registries = registries.map { |registry| [registry.id.to_sym, registry] }.to_h
|
13
13
|
end
|
14
14
|
|
15
15
|
def add_registry(registry)
|
16
|
-
registries[registry.id] = registry
|
16
|
+
registries[registry.id.to_sym] = registry
|
17
17
|
end
|
18
18
|
|
19
19
|
def remove_registry(registry)
|
20
|
-
registries.delete(registry.id)
|
20
|
+
registries.delete(registry.id.to_sym)
|
21
21
|
end
|
22
22
|
|
23
23
|
def get(address, registry_ids: nil)
|
24
|
-
|
25
|
-
|
24
|
+
registry_mask = IdMask.build(registry_ids)
|
25
|
+
target_registries = registry_mask.any? ? all_registries : registry_mask.covering_ids.map { |id| get_registry(id) }.compact
|
26
|
+
|
27
|
+
objects_in_registry = target_registries.map { |registry| registry.get(address, registry_ids: registry_mask.nesting_mask(registry.id)) }.compact
|
26
28
|
objects_in_registry.empty? ? nil : Objects::Merger.new(objects_in_registry).merged_instance
|
27
29
|
end
|
28
30
|
|
@@ -32,11 +34,11 @@ module Yoda
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def get_registry(key)
|
35
|
-
registries[key]
|
37
|
+
registries[key.to_sym]
|
36
38
|
end
|
37
39
|
|
38
40
|
def has_registry(key)
|
39
|
-
registries.has_key?(key)
|
41
|
+
registries.has_key?(key.to_sym)
|
40
42
|
end
|
41
43
|
|
42
44
|
def all_registries
|
@@ -25,10 +25,11 @@ module Yoda
|
|
25
25
|
@composer = composer
|
26
26
|
end
|
27
27
|
|
28
|
-
# @param [String, Symbol]
|
28
|
+
# @param address [String, Symbol]
|
29
|
+
# @param registry_ids [Array<String, Symbol>, nil] if given, search object only from the specified registries.
|
29
30
|
# @return [Objects::Addressable]
|
30
|
-
def get(address)
|
31
|
-
composer.get(address, registry_ids: index.get(address))
|
31
|
+
def get(address, registry_ids: nil)
|
32
|
+
composer.get(address, registry_ids: index.get(address, registry_ids: registry_ids))
|
32
33
|
end
|
33
34
|
|
34
35
|
def add_registry(registry)
|
@@ -74,17 +75,20 @@ module Yoda
|
|
74
75
|
@registry_ids = Set.new(registry_ids)
|
75
76
|
end
|
76
77
|
|
78
|
+
# Return ids of registries which store an object with the address.
|
77
79
|
# @param address [String, Symbol]
|
78
|
-
# @
|
79
|
-
|
80
|
-
|
80
|
+
# @param registry_ids [Array<String, Symbol>, IdMask, nil]
|
81
|
+
# @return [IdMask]
|
82
|
+
def get(address, registry_ids: nil)
|
83
|
+
raw_content = (content[address.to_sym] ||= Objects::SerializableSet.new)
|
84
|
+
registry_ids ? (IdMask.build(registry_ids) & raw_content) : raw_content
|
81
85
|
end
|
82
86
|
|
83
87
|
# @param address [String, Symbol]
|
84
88
|
# @param registry_id [String, Symbol]
|
85
89
|
def add(address, registry_id)
|
86
90
|
content[address.to_sym] ||= Objects::SerializableSet.new
|
87
|
-
content[address.to_sym].add(registry_id)
|
91
|
+
content[address.to_sym].add(registry_id.to_sym)
|
88
92
|
content[address.to_sym].select! { |id| registry_ids.member?(id) }
|
89
93
|
end
|
90
94
|
|
@@ -92,7 +96,7 @@ module Yoda
|
|
92
96
|
# @param registry_id [String, Symbol]
|
93
97
|
def remove(address, registry_id)
|
94
98
|
content[address.to_sym] ||= Objects::SerializableSet.new
|
95
|
-
content[address.to_sym].delete(registry_id)
|
99
|
+
content[address.to_sym].delete(registry_id.to_sym)
|
96
100
|
end
|
97
101
|
|
98
102
|
def keys
|
@@ -100,12 +104,12 @@ module Yoda
|
|
100
104
|
end
|
101
105
|
|
102
106
|
def add_registry(registry)
|
103
|
-
registry_ids.add(registry.id)
|
107
|
+
registry_ids.add(registry.id.to_sym)
|
104
108
|
registry.keys.each { |key| add(key, registry.id) }
|
105
109
|
end
|
106
110
|
|
107
111
|
def remove_registry(registry)
|
108
|
-
registry_ids.delete(registry.id)
|
112
|
+
registry_ids.delete(registry.id.to_sym)
|
109
113
|
end
|
110
114
|
|
111
115
|
def wrap(composer)
|
@@ -8,8 +8,10 @@ module Yoda
|
|
8
8
|
namespace = adapter.namespace(library.name)
|
9
9
|
|
10
10
|
if namespace.empty?
|
11
|
+
Instrument.instance.build_library_registry(name: library.name, version: library.version, message: "Building registry for #{library.name} (#{library.version})")
|
11
12
|
patch = library.create_patch
|
12
13
|
patch && compress_and_save(patch: patch, adapter: namespace)
|
14
|
+
Instrument.instance.build_library_registry(name: library.name, version: library.version, message: "Finished to build registry for #{library.name} (#{library.version})")
|
13
15
|
end
|
14
16
|
|
15
17
|
new(id: library.id, adapter: namespace)
|
@@ -47,7 +49,7 @@ module Yoda
|
|
47
49
|
@adapter = adapter
|
48
50
|
end
|
49
51
|
|
50
|
-
def get(path)
|
52
|
+
def get(path, **)
|
51
53
|
adapter.get(path)
|
52
54
|
end
|
53
55
|
|
data/lib/yoda/store/registry.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'yoda/typing/constant_resolver/query'
|
2
|
+
|
3
|
+
module Yoda
|
4
|
+
module Typing
|
5
|
+
class ConstantResolver
|
6
|
+
class CodeQuery < Query
|
7
|
+
# @return [AST::Vnode]
|
8
|
+
attr_reader :node
|
9
|
+
|
10
|
+
# @return [Types::Type, nil]
|
11
|
+
attr_accessor :result_type
|
12
|
+
|
13
|
+
# @param node [AST::Vnode]
|
14
|
+
def initialize(node:)
|
15
|
+
@node = node
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [nil]
|
19
|
+
def parent
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -12,8 +12,10 @@ module Yoda
|
|
12
12
|
CbaseQuery.new
|
13
13
|
when :empty
|
14
14
|
RelativeBaseQuery.new
|
15
|
-
|
15
|
+
when :const
|
16
16
|
MemberQuery.new(parent: from_node(node.base, tracer: tracer), name: node.name.name.to_s, tracer: tracer && NodeTracer.new(node: node, tracer: tracer))
|
17
|
+
else
|
18
|
+
CodeQuery.new(node: node)
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
@@ -53,6 +55,15 @@ module Yoda
|
|
53
55
|
def tracer
|
54
56
|
nil
|
55
57
|
end
|
58
|
+
|
59
|
+
# @return [Query]
|
60
|
+
def base
|
61
|
+
if parent
|
62
|
+
parent.base
|
63
|
+
else
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
56
67
|
end
|
57
68
|
end
|
58
69
|
end
|
@@ -4,6 +4,7 @@ module Yoda
|
|
4
4
|
module Typing
|
5
5
|
class ConstantResolver
|
6
6
|
require 'yoda/typing/constant_resolver/cbase_query'
|
7
|
+
require 'yoda/typing/constant_resolver/code_query'
|
7
8
|
require 'yoda/typing/constant_resolver/member_query'
|
8
9
|
require 'yoda/typing/constant_resolver/node_tracer'
|
9
10
|
require 'yoda/typing/constant_resolver/query'
|
@@ -29,14 +30,6 @@ module Yoda
|
|
29
30
|
Query.from_node(node, tracer: tracer)
|
30
31
|
end
|
31
32
|
|
32
|
-
# @param node [AST::ConstantNode]
|
33
|
-
# @param tracer [Inferncer::Tracer]
|
34
|
-
# @return [Types::Base]
|
35
|
-
def resolve_node(node, tracer:)
|
36
|
-
query = Query.from_node(node, tracer: tracer)
|
37
|
-
resolve(query)
|
38
|
-
end
|
39
|
-
|
40
33
|
# @param path [String]
|
41
34
|
# @return [Types::Base]
|
42
35
|
def resolve_path(path)
|
@@ -95,6 +88,18 @@ module Yoda
|
|
95
88
|
|
96
89
|
query.tracer&.bind_constants(constants: constants)
|
97
90
|
|
91
|
+
generator.wrap_rbs_type(base_type.value.select_constant_type(query.name.to_s))
|
92
|
+
when CodeQuery
|
93
|
+
base_type = query.parent.result_type
|
94
|
+
|
95
|
+
fail "Result type is not set" unless base_type
|
96
|
+
|
97
|
+
# Remember constant candidates
|
98
|
+
paths = base_type.value.select_constant_paths(query.name.to_s)
|
99
|
+
constants = paths.map { |path| context.environment.resolve_constant(path) }.compact
|
100
|
+
|
101
|
+
query.tracer&.bind_constants(constants: constants)
|
102
|
+
|
98
103
|
generator.wrap_rbs_type(base_type.value.select_constant_type(query.name.to_s))
|
99
104
|
else
|
100
105
|
fail "unexpected"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Yoda
|
2
|
+
module Typing
|
3
|
+
class Inferencer
|
4
|
+
class LoadResolver
|
5
|
+
# @return [Store::Project]
|
6
|
+
attr_reader :project
|
7
|
+
|
8
|
+
# @param project [Store::Project]
|
9
|
+
def initialize(project)
|
10
|
+
@project = project
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param path [String]
|
14
|
+
# @return [String, nil]
|
15
|
+
def resolve(path)
|
16
|
+
path_at_project = Services::LoadablePathResolver.new.find_loadable_path(project.project_load_paths, path)
|
17
|
+
return path_at_project if path_at_project
|
18
|
+
|
19
|
+
found_library = libraries.find do |gem|
|
20
|
+
gem.contain_requirable_file?(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
found_library&.find_requirable_file(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def libraries
|
27
|
+
[
|
28
|
+
# In search priority order
|
29
|
+
project.dependency.loadable_gems,
|
30
|
+
project.dependency.std,
|
31
|
+
project.dependency.core
|
32
|
+
].flatten.map { |lib| lib.with_project_connection(project: project) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -11,6 +11,9 @@ module Yoda
|
|
11
11
|
# @return [Hash{ AST::Node => Symbol }]
|
12
12
|
attr_reader :node_to_kind
|
13
13
|
|
14
|
+
# @return [Hash{ AST::Node => Tree::Base }]
|
15
|
+
attr_reader :node_to_tree
|
16
|
+
|
14
17
|
# @return [Hash{ AST::Node => Types::Base }]
|
15
18
|
attr_reader :node_to_type
|
16
19
|
|
@@ -26,6 +29,9 @@ module Yoda
|
|
26
29
|
# @return [Hash{ AST::Node => Array<Store::Objects::Base> }]
|
27
30
|
attr_reader :node_to_constants
|
28
31
|
|
32
|
+
# @return [Hash{ AST::Node => Array<String> }]
|
33
|
+
attr_reader :node_to_require_paths
|
34
|
+
|
29
35
|
class MaskedMap
|
30
36
|
def initialize
|
31
37
|
@content = {}
|
@@ -59,11 +65,19 @@ module Yoda
|
|
59
65
|
@generator = generator
|
60
66
|
|
61
67
|
@node_to_kind = MaskedMap.new
|
68
|
+
@node_to_tree = MaskedMap.new
|
62
69
|
@node_to_type = MaskedMap.new
|
63
70
|
@node_to_context = MaskedMap.new
|
64
71
|
@node_to_method_candidates = MaskedMap.new
|
65
72
|
@node_to_receiver_type = MaskedMap.new
|
66
73
|
@node_to_constants = MaskedMap.new
|
74
|
+
@node_to_require_paths = MaskedMap.new
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param node [AST::Node]
|
78
|
+
# @param tree [Tree::Base]
|
79
|
+
def bind_tree(node:, tree:)
|
80
|
+
node_to_tree[node.identifier] = tree
|
67
81
|
end
|
68
82
|
|
69
83
|
# @param node [AST::Node]
|
@@ -112,12 +126,24 @@ module Yoda
|
|
112
126
|
node_to_constants[node.identifier] = constants
|
113
127
|
end
|
114
128
|
|
129
|
+
# @param node [AST::Node]
|
130
|
+
# @param require_paths [Array<String>]
|
131
|
+
def bind_require_paths(node:, require_paths:)
|
132
|
+
node_to_require_paths[node.identifier] = require_paths
|
133
|
+
end
|
134
|
+
|
115
135
|
# @param node [AST::Node]
|
116
136
|
# @return [Symbol, nil]
|
117
137
|
def kind(node)
|
118
138
|
node_to_kind[node.identifier]
|
119
139
|
end
|
120
140
|
|
141
|
+
# @param node [AST::Node]
|
142
|
+
# @return [Tree::Base, nil]
|
143
|
+
def tree(node)
|
144
|
+
node_to_tree[node.identifier]
|
145
|
+
end
|
146
|
+
|
121
147
|
# @param node [AST::Node]
|
122
148
|
# @return [Types::Type]
|
123
149
|
def type(node)
|
@@ -154,6 +180,12 @@ module Yoda
|
|
154
180
|
node_to_constants[node.identifier] || []
|
155
181
|
end
|
156
182
|
|
183
|
+
# @param node [AST::Node]
|
184
|
+
# @return [Array<String>]
|
185
|
+
def require_paths(node)
|
186
|
+
node_to_require_paths[node.identifier] || []
|
187
|
+
end
|
188
|
+
|
157
189
|
# @param node [AST::Node]
|
158
190
|
# @return [Contexts::BaseContext, nil]
|
159
191
|
def context(node)
|
@@ -3,7 +3,7 @@ module Yoda
|
|
3
3
|
class Inferencer
|
4
4
|
require 'yoda/typing/inferencer/arguments_binder'
|
5
5
|
require 'yoda/typing/inferencer/arguments'
|
6
|
-
require 'yoda/typing/inferencer/
|
6
|
+
require 'yoda/typing/inferencer/load_resolver'
|
7
7
|
require 'yoda/typing/inferencer/method_resolver'
|
8
8
|
require 'yoda/typing/inferencer/object_resolver'
|
9
9
|
require 'yoda/typing/inferencer/parameter_binder'
|
@@ -33,7 +33,8 @@ module Yoda
|
|
33
33
|
# @param node [AST::Vnode]
|
34
34
|
# @return [Store::Types::Base]
|
35
35
|
def infer(node)
|
36
|
-
AstTraverser.new(tracer: tracer, context: context).traverse(node)
|
36
|
+
# AstTraverser.new(tracer: tracer, context: context).traverse(node)
|
37
|
+
Tree.build(node, context: context, tracer: tracer).type
|
37
38
|
end
|
38
39
|
|
39
40
|
# @param pp [PP]
|
@@ -1,44 +1,89 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'pp'
|
3
|
+
|
1
4
|
module Yoda
|
2
5
|
module Typing
|
3
6
|
module Tree
|
4
7
|
# @abstract
|
5
8
|
class Base
|
6
|
-
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
# @return [AST::Vnode]
|
7
12
|
attr_reader :node
|
8
13
|
|
9
|
-
# @return [
|
14
|
+
# @return [Inferencer::Tracer]
|
15
|
+
attr_reader :tracer
|
16
|
+
|
17
|
+
# @return [Contexts::BaseContext]
|
10
18
|
attr_reader :context
|
11
19
|
|
12
|
-
|
13
|
-
|
20
|
+
delegate [:bind_tree, :bind_context, :bind_type, :bind_send, :bind_method_definition, :bind_require_paths] => :tracer
|
21
|
+
|
22
|
+
# @return [Types::Generator]
|
23
|
+
delegate [:generator] => :context
|
24
|
+
|
25
|
+
# @param node [AST::Vnode]
|
26
|
+
# @param tracer [Inferencer::Tracer]
|
27
|
+
# @param context [Contexts::BaseContext]
|
14
28
|
# @param parent [Base, nil]
|
15
|
-
def initialize(node:, context:, parent: nil)
|
29
|
+
def initialize(node:, tracer:, context:, parent: nil)
|
16
30
|
@node = node
|
31
|
+
@tracer = tracer
|
17
32
|
@context = context
|
18
33
|
@parent = parent
|
19
34
|
end
|
20
35
|
|
21
|
-
# @
|
22
|
-
|
23
|
-
|
24
|
-
|
36
|
+
# @return [Types::Type]
|
37
|
+
def type
|
38
|
+
@type ||= begin
|
39
|
+
bind_tree(node: node, tree: self)
|
40
|
+
bind_context(node: node, context: context)
|
41
|
+
Logger.trace("Traversing #{node}")
|
42
|
+
type = infer_type
|
43
|
+
Logger.trace("Traversed #{node} -> #{type.to_s}")
|
44
|
+
bind_type(node: node, type: type, context: context)
|
45
|
+
|
46
|
+
type
|
47
|
+
end
|
25
48
|
end
|
26
49
|
|
27
|
-
# @
|
28
|
-
# @return [
|
29
|
-
def
|
30
|
-
|
50
|
+
# @param node [AST::Vnode]
|
51
|
+
# @return [Base]
|
52
|
+
def build_child(node, context: self.context)
|
53
|
+
Tree.build(node, context: context, tracer: tracer, parent: self)
|
31
54
|
end
|
32
55
|
|
33
|
-
# @
|
34
|
-
|
35
|
-
|
56
|
+
# @param node [AST::Vnode]
|
57
|
+
# @param (see #build_child)
|
58
|
+
# @return [Types::Type]
|
59
|
+
def infer_child(node, **kwargs)
|
60
|
+
build_child(node, **kwargs).type
|
36
61
|
end
|
37
62
|
|
38
|
-
# @param
|
39
|
-
|
40
|
-
|
41
|
-
|
63
|
+
# @param pp [PP]
|
64
|
+
def pretty_print(pp)
|
65
|
+
pp.object_group(self) do
|
66
|
+
pp.breakable
|
67
|
+
pp.text "@node="
|
68
|
+
pp.pp node
|
69
|
+
pp.text "@context="
|
70
|
+
pp.pp context
|
71
|
+
pp.comma_breakable
|
72
|
+
pp.text "@tracer="
|
73
|
+
pp.pp tracer
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def inspect
|
78
|
+
pretty_print_inspect
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# @abstract
|
84
|
+
# @return [Types::Type]
|
85
|
+
def infer_type
|
86
|
+
fail NotImplementedError
|
42
87
|
end
|
43
88
|
end
|
44
89
|
end
|
@@ -2,12 +2,12 @@ module Yoda
|
|
2
2
|
module Typing
|
3
3
|
module Tree
|
4
4
|
class Begin < Base
|
5
|
-
|
6
|
-
|
7
|
-
end
|
5
|
+
# @!method node
|
6
|
+
# @return [AST::BlockNode]
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
# @return [Types::Type]
|
9
|
+
def infer_type
|
10
|
+
node.children.map { |node| infer_child(node) }.last
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yoda/typing/tree/send_inferable'
|
2
|
+
|
3
|
+
module Yoda
|
4
|
+
module Typing
|
5
|
+
module Tree
|
6
|
+
class BlockCall < Base
|
7
|
+
include SendInferable
|
8
|
+
|
9
|
+
# @!method node
|
10
|
+
# @return [AST::BlockCallNode]
|
11
|
+
|
12
|
+
# @return [Types::Type]
|
13
|
+
def infer_type
|
14
|
+
if node.send_clause.type == :send
|
15
|
+
infer_send(node.send_clause, node.parameters, node.body)
|
16
|
+
else
|
17
|
+
# super or zsuper
|
18
|
+
child_type = infer_child(node.send_clause)
|
19
|
+
infer_child(node.body)
|
20
|
+
child_type
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -2,26 +2,15 @@ module Yoda
|
|
2
2
|
module Typing
|
3
3
|
module Tree
|
4
4
|
class Case < Base
|
5
|
-
#
|
6
|
-
|
7
|
-
# TODO
|
8
|
-
Types::Union.new([*when_body_nodes, else_node].map { |node| infer(node) })
|
9
|
-
end
|
10
|
-
|
11
|
-
def children
|
12
|
-
@children ||= [subject, *when_branches, else_branch]
|
13
|
-
end
|
14
|
-
|
15
|
-
def subject
|
16
|
-
@subject ||= build_child(node.children.first)
|
17
|
-
end
|
18
|
-
|
19
|
-
def when_branches
|
20
|
-
@when_branches ||= node.children.slice(1, -2).map(&method(:build_child))
|
21
|
-
end
|
5
|
+
# @!method node
|
6
|
+
# @return [AST::CaseNode]
|
22
7
|
|
23
|
-
|
24
|
-
|
8
|
+
# @return [Type::Type]
|
9
|
+
def infer_type
|
10
|
+
subject_node, *when_nodes, else_node = node.children
|
11
|
+
infer_child(subject_node)
|
12
|
+
when_body_nodes = when_nodes.map { |node| node.children.last }
|
13
|
+
generator.union_type(*[*when_body_nodes, else_node].compact.map { |node| infer_child(node) })
|
25
14
|
end
|
26
15
|
end
|
27
16
|
end
|
@@ -1,29 +1,21 @@
|
|
1
|
+
require 'yoda/typing/tree/namespace_inferable'
|
2
|
+
|
1
3
|
module Yoda
|
2
4
|
module Typing
|
3
5
|
module Tree
|
4
6
|
class ClassTree < Base
|
5
|
-
|
6
|
-
infer_namespace_node(node)
|
7
|
-
end
|
7
|
+
include NamespaceInferable
|
8
8
|
|
9
|
-
#
|
10
|
-
#
|
11
|
-
def infer_namespace_node(node)
|
12
|
-
case node.type
|
13
|
-
when :module
|
14
|
-
name_node, block_node = node.children
|
15
|
-
when :class
|
16
|
-
name_node, _, block_node = node.children
|
17
|
-
end
|
18
|
-
constant_resolver = ConstantResolver.new(context: context, node: name_node)
|
19
|
-
type = constant_resolver.resolve_constant_type
|
20
|
-
block_context = NamespaceContext.new(objects: [constant_resolver.constant], parent: context, registry: context.registry, receiver: type)
|
9
|
+
# @!method node
|
10
|
+
# @return [AST::ClassNode]
|
21
11
|
|
22
|
-
|
23
|
-
|
12
|
+
# @return [Types::Base]
|
13
|
+
def infer_type
|
14
|
+
if super_class_node = node.super_class
|
15
|
+
infer_child(super_class_node)
|
24
16
|
end
|
25
17
|
|
26
|
-
|
18
|
+
infer_namespace
|
27
19
|
end
|
28
20
|
end
|
29
21
|
end
|