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.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +22 -27
  4. data/client/vscode/README.md +17 -2
  5. data/client/vscode/package-lock.json +908 -817
  6. data/client/vscode/package.json +17 -7
  7. data/client/vscode/src/check-versions.ts +49 -0
  8. data/client/vscode/src/config.ts +8 -1
  9. data/client/vscode/src/extension.ts +4 -3
  10. data/client/vscode/src/install-tools.ts +56 -16
  11. data/client/vscode/src/language-server.ts +5 -0
  12. data/client/vscode/src/test/runTest.ts +1 -1
  13. data/client/vscode/src/test/suite/hover.test.ts +13 -25
  14. data/client/vscode/src/test/suite/index.ts +1 -2
  15. data/client/vscode/src/utils.ts +11 -0
  16. data/images/hover-method.png +0 -0
  17. data/images/method-complete.png +0 -0
  18. data/lib/yoda/cli/analyze_deps.rb +46 -25
  19. data/lib/yoda/id_mask.rb +84 -0
  20. data/lib/yoda/instrument.rb +7 -0
  21. data/lib/yoda/model/descriptions/require_path_description.rb +45 -0
  22. data/lib/yoda/model/descriptions.rb +1 -0
  23. data/lib/yoda/model/node_signatures/node.rb +9 -1
  24. data/lib/yoda/model/values/literal_value.rb +3 -0
  25. data/lib/yoda/parsing/location.rb +9 -0
  26. data/lib/yoda/server/concurrent_writer.rb +1 -1
  27. data/lib/yoda/server/lifecycle_handler.rb +101 -78
  28. data/lib/yoda/server/notifier.rb +2 -55
  29. data/lib/yoda/services/loadable_path_resolver.rb +40 -0
  30. data/lib/yoda/services.rb +1 -0
  31. data/lib/yoda/store/actions/read_project_files.rb +9 -7
  32. data/lib/yoda/store/adapters/gdbm_adapter/namespace_accessor.rb +1 -1
  33. data/lib/yoda/store/adapters/memory_adapter.rb +1 -1
  34. data/lib/yoda/store/objects/libraries_status.rb +1 -1
  35. data/lib/yoda/store/objects/library/core.rb +8 -0
  36. data/lib/yoda/store/objects/library/gem.rb +14 -3
  37. data/lib/yoda/store/objects/library/path_resolvable.rb +29 -0
  38. data/lib/yoda/store/objects/library/std.rb +9 -0
  39. data/lib/yoda/store/objects/library.rb +1 -0
  40. data/lib/yoda/store/objects/patch.rb +1 -1
  41. data/lib/yoda/store/objects/patch_set.rb +2 -2
  42. data/lib/yoda/store/project/dependency.rb +22 -4
  43. data/lib/yoda/store/project/file_finder.rb +20 -0
  44. data/lib/yoda/store/project.rb +2 -0
  45. data/lib/yoda/store/registry/cache.rb +2 -2
  46. data/lib/yoda/store/registry/composer.rb +9 -7
  47. data/lib/yoda/store/registry/index.rb +14 -10
  48. data/lib/yoda/store/registry/library_registry.rb +3 -1
  49. data/lib/yoda/store/registry.rb +1 -1
  50. data/lib/yoda/typing/constant_resolver/code_query.rb +25 -0
  51. data/lib/yoda/typing/constant_resolver/query.rb +12 -1
  52. data/lib/yoda/typing/constant_resolver.rb +13 -8
  53. data/lib/yoda/typing/inferencer/load_resolver.rb +37 -0
  54. data/lib/yoda/typing/inferencer/tracer.rb +32 -0
  55. data/lib/yoda/typing/inferencer.rb +3 -2
  56. data/lib/yoda/typing/node_info.rb +5 -0
  57. data/lib/yoda/typing/tree/{defined.rb → ask_defined.rb} +3 -2
  58. data/lib/yoda/typing/tree/base.rb +65 -20
  59. data/lib/yoda/typing/tree/begin.rb +5 -5
  60. data/lib/yoda/typing/tree/block_call.rb +26 -0
  61. data/lib/yoda/typing/tree/case.rb +8 -19
  62. data/lib/yoda/typing/tree/class_tree.rb +10 -18
  63. data/lib/yoda/typing/tree/conditional_loop.rb +15 -0
  64. data/lib/yoda/typing/tree/constant.rb +19 -0
  65. data/lib/yoda/typing/tree/constant_assignment.rb +2 -2
  66. data/lib/yoda/typing/tree/ensure.rb +17 -0
  67. data/lib/yoda/typing/tree/for.rb +7 -0
  68. data/lib/yoda/typing/tree/hash_tree.rb +32 -0
  69. data/lib/yoda/typing/tree/if.rb +10 -5
  70. data/lib/yoda/typing/tree/interpolation_text.rb +21 -0
  71. data/lib/yoda/typing/tree/literal.rb +8 -36
  72. data/lib/yoda/typing/tree/literal_inferable.rb +48 -0
  73. data/lib/yoda/typing/tree/local_exit.rb +15 -0
  74. data/lib/yoda/typing/tree/logical_assignment.rb +5 -5
  75. data/lib/yoda/typing/tree/logical_operator.rb +6 -5
  76. data/lib/yoda/typing/tree/method_def.rb +41 -0
  77. data/lib/yoda/typing/tree/method_inferable.rb +51 -0
  78. data/lib/yoda/typing/tree/module_tree.rb +7 -20
  79. data/lib/yoda/typing/tree/multiple_assignment.rb +6 -10
  80. data/lib/yoda/typing/tree/namespace_inferable.rb +20 -0
  81. data/lib/yoda/typing/tree/rescue.rb +18 -0
  82. data/lib/yoda/typing/tree/rescue_clause.rb +42 -0
  83. data/lib/yoda/typing/tree/self.rb +2 -1
  84. data/lib/yoda/typing/tree/send.rb +8 -60
  85. data/lib/yoda/typing/tree/send_inferable.rb +89 -0
  86. data/lib/yoda/typing/tree/singleton_class_tree.rb +24 -0
  87. data/lib/yoda/typing/tree/singleton_method_def.rb +41 -0
  88. data/lib/yoda/typing/tree/super.rb +9 -2
  89. data/lib/yoda/typing/tree/variable.rb +5 -10
  90. data/lib/yoda/typing/tree/variable_assignment.rb +11 -8
  91. data/lib/yoda/typing/tree/yield.rb +9 -2
  92. data/lib/yoda/typing/tree.rb +55 -22
  93. data/lib/yoda/typing.rb +1 -0
  94. data/lib/yoda/version.rb +1 -1
  95. data/lib/yoda.rb +1 -0
  96. data/yoda-language-server.gemspec +1 -1
  97. metadata +35 -18
  98. data/lib/yoda/typing/inferencer/ast_traverser.rb +0 -408
  99. data/lib/yoda/typing/tree/block.rb +0 -12
  100. data/lib/yoda/typing/tree/const.rb +0 -12
  101. data/lib/yoda/typing/tree/escape.rb +0 -12
  102. data/lib/yoda/typing/tree/hash_body.rb +0 -36
  103. data/lib/yoda/typing/tree/literal_with_interpolation.rb +0 -21
  104. data/lib/yoda/typing/tree/method.rb +0 -43
  105. data/lib/yoda/typing/tree/rescue_body.rb +0 -12
  106. data/lib/yoda/typing/tree/singleton_method.rb +0 -47
  107. 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
@@ -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)
@@ -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
 
@@ -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
@@ -2,26 +2,15 @@ module Yoda
2
2
  module Typing
3
3
  module Tree
4
4
  class Case < Base
5
- # @return [[Store::Types::Base, Environment]]
6
- def infer_case_node
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
- def else_branch
24
- @else_branch ||= build_child(node.children.last)
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
- def type
6
- infer_namespace_node(node)
7
- end
7
+ include NamespaceInferable
8
8
 
9
- # @param node [::AST::Node]
10
- # @return [Types::Base]
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
- if block_node
23
- derive(context: block_context).infer(block_node)
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
- type
18
+ infer_namespace
27
19
  end
28
20
  end
29
21
  end