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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/client/vscode/package-lock.json +6 -6
  4. data/client/vscode/src/check-versions.ts +5 -11
  5. data/client/vscode/src/install-tools.ts +1 -1
  6. data/lib/yoda/cli/analyze_deps.rb +46 -25
  7. data/lib/yoda/id_mask.rb +84 -0
  8. data/lib/yoda/model/descriptions/require_path_description.rb +45 -0
  9. data/lib/yoda/model/descriptions.rb +1 -0
  10. data/lib/yoda/model/node_signatures/node.rb +9 -1
  11. data/lib/yoda/model/values/literal_value.rb +3 -0
  12. data/lib/yoda/parsing/location.rb +9 -0
  13. data/lib/yoda/services/loadable_path_resolver.rb +33 -0
  14. data/lib/yoda/services.rb +1 -0
  15. data/lib/yoda/store/actions/read_project_files.rb +9 -7
  16. data/lib/yoda/store/adapters/gdbm_adapter/namespace_accessor.rb +1 -1
  17. data/lib/yoda/store/adapters/memory_adapter.rb +1 -1
  18. data/lib/yoda/store/objects/libraries_status.rb +1 -1
  19. data/lib/yoda/store/objects/library/core.rb +8 -0
  20. data/lib/yoda/store/objects/library/gem.rb +14 -3
  21. data/lib/yoda/store/objects/library/path_resolvable.rb +29 -0
  22. data/lib/yoda/store/objects/library/std.rb +9 -0
  23. data/lib/yoda/store/objects/library.rb +1 -0
  24. data/lib/yoda/store/objects/patch.rb +1 -1
  25. data/lib/yoda/store/objects/patch_set.rb +2 -2
  26. data/lib/yoda/store/project/dependency.rb +22 -4
  27. data/lib/yoda/store/project/file_finder.rb +20 -0
  28. data/lib/yoda/store/project.rb +2 -0
  29. data/lib/yoda/store/registry/cache.rb +2 -2
  30. data/lib/yoda/store/registry/composer.rb +9 -7
  31. data/lib/yoda/store/registry/index.rb +14 -10
  32. data/lib/yoda/store/registry/library_registry.rb +1 -1
  33. data/lib/yoda/store/registry.rb +1 -1
  34. data/lib/yoda/typing/constant_resolver/code_query.rb +25 -0
  35. data/lib/yoda/typing/constant_resolver/query.rb +12 -1
  36. data/lib/yoda/typing/constant_resolver.rb +13 -8
  37. data/lib/yoda/typing/inferencer/load_resolver.rb +37 -0
  38. data/lib/yoda/typing/inferencer/tracer.rb +32 -0
  39. data/lib/yoda/typing/inferencer.rb +3 -2
  40. data/lib/yoda/typing/node_info.rb +5 -0
  41. data/lib/yoda/typing/tree/{defined.rb → ask_defined.rb} +3 -2
  42. data/lib/yoda/typing/tree/base.rb +65 -20
  43. data/lib/yoda/typing/tree/begin.rb +5 -5
  44. data/lib/yoda/typing/tree/block_call.rb +26 -0
  45. data/lib/yoda/typing/tree/case.rb +8 -19
  46. data/lib/yoda/typing/tree/class_tree.rb +10 -18
  47. data/lib/yoda/typing/tree/conditional_loop.rb +15 -0
  48. data/lib/yoda/typing/tree/constant.rb +19 -0
  49. data/lib/yoda/typing/tree/constant_assignment.rb +2 -2
  50. data/lib/yoda/typing/tree/ensure.rb +17 -0
  51. data/lib/yoda/typing/tree/for.rb +7 -0
  52. data/lib/yoda/typing/tree/hash_tree.rb +32 -0
  53. data/lib/yoda/typing/tree/if.rb +10 -5
  54. data/lib/yoda/typing/tree/interpolation_text.rb +21 -0
  55. data/lib/yoda/typing/tree/literal.rb +8 -36
  56. data/lib/yoda/typing/tree/literal_inferable.rb +48 -0
  57. data/lib/yoda/typing/tree/local_exit.rb +15 -0
  58. data/lib/yoda/typing/tree/logical_assignment.rb +5 -5
  59. data/lib/yoda/typing/tree/logical_operator.rb +6 -5
  60. data/lib/yoda/typing/tree/method_def.rb +41 -0
  61. data/lib/yoda/typing/tree/method_inferable.rb +51 -0
  62. data/lib/yoda/typing/tree/module_tree.rb +7 -20
  63. data/lib/yoda/typing/tree/multiple_assignment.rb +6 -10
  64. data/lib/yoda/typing/tree/namespace_inferable.rb +20 -0
  65. data/lib/yoda/typing/tree/rescue.rb +18 -0
  66. data/lib/yoda/typing/tree/rescue_clause.rb +42 -0
  67. data/lib/yoda/typing/tree/self.rb +2 -1
  68. data/lib/yoda/typing/tree/send.rb +8 -60
  69. data/lib/yoda/typing/tree/send_inferable.rb +89 -0
  70. data/lib/yoda/typing/tree/singleton_class_tree.rb +24 -0
  71. data/lib/yoda/typing/tree/singleton_method_def.rb +41 -0
  72. data/lib/yoda/typing/tree/super.rb +9 -2
  73. data/lib/yoda/typing/tree/variable.rb +5 -10
  74. data/lib/yoda/typing/tree/variable_assignment.rb +11 -8
  75. data/lib/yoda/typing/tree/yield.rb +9 -2
  76. data/lib/yoda/typing/tree.rb +55 -22
  77. data/lib/yoda/typing.rb +1 -0
  78. data/lib/yoda/version.rb +1 -1
  79. data/lib/yoda.rb +1 -0
  80. metadata +25 -13
  81. data/lib/yoda/typing/inferencer/ast_traverser.rb +0 -408
  82. data/lib/yoda/typing/tree/block.rb +0 -12
  83. data/lib/yoda/typing/tree/const.rb +0 -12
  84. data/lib/yoda/typing/tree/escape.rb +0 -12
  85. data/lib/yoda/typing/tree/hash_body.rb +0 -36
  86. data/lib/yoda/typing/tree/literal_with_interpolation.rb +0 -21
  87. data/lib/yoda/typing/tree/method.rb +0 -43
  88. data/lib/yoda/typing/tree/rescue_body.rb +0 -12
  89. data/lib/yoda/typing/tree/singleton_method.rb +0 -47
  90. 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 gems
16
- builder.gems
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 gems
51
- @gems ||= begin
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
@@ -33,6 +33,8 @@ module Yoda
33
33
  :library_registry_dir_path,
34
34
  :gemfile_lock_path,
35
35
  :config_file_path,
36
+ :project_source_paths,
37
+ :project_load_paths,
36
38
  :readable_config_file_path,
37
39
  :library_local_yardoc_path,
38
40
  :library_registry_path,
@@ -19,9 +19,9 @@ module Yoda
19
19
  @cache = cache
20
20
  end
21
21
 
22
- def get(key)
22
+ def get(key, **kwargs)
23
23
  @cache.fetch_or_calc(key) do
24
- @registry.get(key)
24
+ @registry.get(key, **kwargs)
25
25
  end
26
26
  end
27
27
 
@@ -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
- target_registries = registry_ids ? registry_ids.map { |id| get_registry(id) }.compact : all_registries
25
- objects_in_registry = target_registries.map { |registry| registry.get(address) }.compact
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
- # @return [Set<Symbol>]
79
- def get(address)
80
- content[address.to_sym] ||= Objects::SerializableSet.new
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)
@@ -49,7 +49,7 @@ module Yoda
49
49
  @adapter = adapter
50
50
  end
51
51
 
52
- def get(path)
52
+ def get(path, **)
53
53
  adapter.get(path)
54
54
  end
55
55
 
@@ -12,7 +12,7 @@ module Yoda
12
12
  require 'yoda/store/registry/project_registry'
13
13
 
14
14
  # @note This number must be updated when breaking change is added.
15
- REGISTRY_VERSION = 6
15
+ REGISTRY_VERSION = 7
16
16
 
17
17
  class << self
18
18
  def registry_name
@@ -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
- else
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/ast_traverser'
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]
@@ -41,6 +41,11 @@ module Yoda
41
41
  tracer.constants(node)
42
42
  end
43
43
 
44
+ # @return [Array<String>]
45
+ def require_paths
46
+ tracer.require_paths(node)
47
+ end
48
+
44
49
  # @return [Types::Type]
45
50
  def type
46
51
  tracer.type(node)
@@ -1,8 +1,9 @@
1
1
  module Yoda
2
2
  module Typing
3
3
  module Tree
4
- class Escape < Base
5
- def type
4
+ class AskDefined < Base
5
+ # @return [Types::Type]
6
+ def infer_type
6
7
  generator.boolean_type
7
8
  end
8
9
  end
@@ -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
- # @return [::AST::Node]
9
+ extend Forwardable
10
+
11
+ # @return [AST::Vnode]
7
12
  attr_reader :node
8
13
 
9
- # @return [BaseContext]
14
+ # @return [Inferencer::Tracer]
15
+ attr_reader :tracer
16
+
17
+ # @return [Contexts::BaseContext]
10
18
  attr_reader :context
11
19
 
12
- # @param node [::AST::Node]
13
- # @param context [BaseContext]
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
- # @abstract
22
- # @return [Array<Base>]
23
- def children
24
- fail NotImplementedError
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
- # @abstract
28
- # @return [Types::Base]
29
- def type
30
- fail NotImplementedError
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
- # @return [Types::Generator]
34
- def generator
35
- @generator ||= Types::Generator.new(context.registry)
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 node [::AST::Node]
39
- # @return [Base]
40
- def build_child(node, context: nil)
41
- Tree.build(node, context: context, parent: self)
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
- def children
6
- @children ||= node.children.map(&method(:build_child))
7
- end
5
+ # @!method node
6
+ # @return [AST::BlockNode]
8
7
 
9
- def type
10
- children.map(&:type).last
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