yoda-language-server 0.9.0 → 0.10.0
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/CHANGELOG.md +5 -0
- data/client/vscode/package-lock.json +6 -6
- data/client/vscode/src/check-versions.ts +5 -11
- data/client/vscode/src/install-tools.ts +1 -1
- data/lib/yoda/cli/analyze_deps.rb +46 -25
- data/lib/yoda/id_mask.rb +84 -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/services/loadable_path_resolver.rb +33 -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 +1 -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
- metadata +25 -13
- 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
@@ -13,7 +13,7 @@ module Yoda
|
|
13
13
|
|
14
14
|
# @param address [Symbol]
|
15
15
|
# @return [Set<Symbol>]
|
16
|
-
def get(address)
|
16
|
+
def get(address, **)
|
17
17
|
index[address] ||= Set.new
|
18
18
|
end
|
19
19
|
|
@@ -73,7 +73,7 @@ module Yoda
|
|
73
73
|
|
74
74
|
# @param address [String, Symbol]
|
75
75
|
# @return [Addressable, nil]
|
76
|
-
def find(address)
|
76
|
+
def find(address, **)
|
77
77
|
if (patches = get_patches(address)).empty?
|
78
78
|
nil
|
79
79
|
else
|
@@ -12,8 +12,13 @@ module Yoda
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# @return [Array<Objects::Library::Gem>]
|
15
|
-
def
|
16
|
-
builder.
|
15
|
+
def loadable_gems
|
16
|
+
builder.loadable_gems
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<Objects::Library::Gem>]
|
20
|
+
def autoload_gems
|
21
|
+
builder.autoload_gems
|
17
22
|
end
|
18
23
|
|
19
24
|
# @param name [String]
|
@@ -33,6 +38,7 @@ module Yoda
|
|
33
38
|
@std ||= Objects::Library.std
|
34
39
|
end
|
35
40
|
|
41
|
+
# @return [Builder]
|
36
42
|
def builder
|
37
43
|
@builder ||= Builder.new(project)
|
38
44
|
end
|
@@ -47,14 +53,21 @@ module Yoda
|
|
47
53
|
end
|
48
54
|
|
49
55
|
# @return [Array<Objects::Library::Gem>]
|
50
|
-
def
|
51
|
-
@
|
56
|
+
def loadable_gems
|
57
|
+
@loadable_gems ||= begin
|
52
58
|
dependencies
|
53
59
|
.map { |attrs| Objects::Library::Gem.new(**attrs) }
|
54
60
|
.reject { |spec| project.config.ignored_gems.include?(spec.name) }
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
64
|
+
# @return [Array<Objects::Library::Gem>]
|
65
|
+
def autoload_gems
|
66
|
+
@autoload_gems ||= begin
|
67
|
+
loadable_gems.select { |gem| autoload_dependency_ids.include?(gem.id) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
58
71
|
private
|
59
72
|
|
60
73
|
# @return [Array<Hash>]
|
@@ -62,6 +75,11 @@ module Yoda
|
|
62
75
|
analyzed_deps[:dependencies] || []
|
63
76
|
end
|
64
77
|
|
78
|
+
# @return [Array<String>]
|
79
|
+
def autoload_dependency_ids
|
80
|
+
analyzed_deps[:autoload_dependency_ids] || []
|
81
|
+
end
|
82
|
+
|
65
83
|
# @return [Hash]
|
66
84
|
def analyzed_deps
|
67
85
|
@analyzed_deps ||= begin
|
@@ -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)
|
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
|