steep-activesupport-4 1.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +1032 -0
  5. data/LICENSE +21 -0
  6. data/README.md +260 -0
  7. data/Rakefile +227 -0
  8. data/Steepfile +68 -0
  9. data/bin/console +14 -0
  10. data/bin/generate-diagnostics-docs.rb +112 -0
  11. data/bin/mem_graph.rb +67 -0
  12. data/bin/mem_prof.rb +102 -0
  13. data/bin/output_rebaseline.rb +34 -0
  14. data/bin/output_test.rb +60 -0
  15. data/bin/rbs +20 -0
  16. data/bin/rbs-inline +19 -0
  17. data/bin/setup +9 -0
  18. data/bin/stackprof_test.rb +19 -0
  19. data/bin/steep +19 -0
  20. data/bin/steep-check.rb +251 -0
  21. data/bin/steep-prof +16 -0
  22. data/doc/narrowing.md +195 -0
  23. data/doc/shape.md +194 -0
  24. data/exe/steep +18 -0
  25. data/guides/README.md +5 -0
  26. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
  27. data/guides/src/getting-started/getting-started.md +163 -0
  28. data/guides/src/nil-optional/nil-optional.md +195 -0
  29. data/lib/steep/annotation_parser.rb +199 -0
  30. data/lib/steep/ast/annotation/collection.rb +172 -0
  31. data/lib/steep/ast/annotation.rb +137 -0
  32. data/lib/steep/ast/builtin.rb +104 -0
  33. data/lib/steep/ast/ignore.rb +148 -0
  34. data/lib/steep/ast/node/type_application.rb +88 -0
  35. data/lib/steep/ast/node/type_assertion.rb +81 -0
  36. data/lib/steep/ast/types/any.rb +35 -0
  37. data/lib/steep/ast/types/boolean.rb +45 -0
  38. data/lib/steep/ast/types/bot.rb +35 -0
  39. data/lib/steep/ast/types/class.rb +43 -0
  40. data/lib/steep/ast/types/factory.rb +557 -0
  41. data/lib/steep/ast/types/helper.rb +40 -0
  42. data/lib/steep/ast/types/instance.rb +42 -0
  43. data/lib/steep/ast/types/intersection.rb +93 -0
  44. data/lib/steep/ast/types/literal.rb +59 -0
  45. data/lib/steep/ast/types/logic.rb +84 -0
  46. data/lib/steep/ast/types/name.rb +128 -0
  47. data/lib/steep/ast/types/nil.rb +41 -0
  48. data/lib/steep/ast/types/proc.rb +117 -0
  49. data/lib/steep/ast/types/record.rb +79 -0
  50. data/lib/steep/ast/types/self.rb +43 -0
  51. data/lib/steep/ast/types/shared_instance.rb +11 -0
  52. data/lib/steep/ast/types/top.rb +35 -0
  53. data/lib/steep/ast/types/tuple.rb +60 -0
  54. data/lib/steep/ast/types/union.rb +97 -0
  55. data/lib/steep/ast/types/var.rb +65 -0
  56. data/lib/steep/ast/types/void.rb +35 -0
  57. data/lib/steep/cli.rb +401 -0
  58. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  59. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  60. data/lib/steep/diagnostic/helper.rb +18 -0
  61. data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
  62. data/lib/steep/diagnostic/result_printer2.rb +48 -0
  63. data/lib/steep/diagnostic/ruby.rb +1221 -0
  64. data/lib/steep/diagnostic/signature.rb +570 -0
  65. data/lib/steep/drivers/annotations.rb +52 -0
  66. data/lib/steep/drivers/check.rb +339 -0
  67. data/lib/steep/drivers/checkfile.rb +210 -0
  68. data/lib/steep/drivers/diagnostic_printer.rb +105 -0
  69. data/lib/steep/drivers/init.rb +66 -0
  70. data/lib/steep/drivers/langserver.rb +56 -0
  71. data/lib/steep/drivers/print_project.rb +113 -0
  72. data/lib/steep/drivers/stats.rb +203 -0
  73. data/lib/steep/drivers/utils/driver_helper.rb +143 -0
  74. data/lib/steep/drivers/utils/jobs_option.rb +26 -0
  75. data/lib/steep/drivers/vendor.rb +27 -0
  76. data/lib/steep/drivers/watch.rb +194 -0
  77. data/lib/steep/drivers/worker.rb +58 -0
  78. data/lib/steep/equatable.rb +23 -0
  79. data/lib/steep/expectations.rb +228 -0
  80. data/lib/steep/index/rbs_index.rb +350 -0
  81. data/lib/steep/index/signature_symbol_provider.rb +185 -0
  82. data/lib/steep/index/source_index.rb +167 -0
  83. data/lib/steep/interface/block.rb +103 -0
  84. data/lib/steep/interface/builder.rb +843 -0
  85. data/lib/steep/interface/function.rb +1090 -0
  86. data/lib/steep/interface/method_type.rb +330 -0
  87. data/lib/steep/interface/shape.rb +239 -0
  88. data/lib/steep/interface/substitution.rb +159 -0
  89. data/lib/steep/interface/type_param.rb +115 -0
  90. data/lib/steep/located_value.rb +20 -0
  91. data/lib/steep/method_name.rb +42 -0
  92. data/lib/steep/module_helper.rb +24 -0
  93. data/lib/steep/node_helper.rb +273 -0
  94. data/lib/steep/path_helper.rb +30 -0
  95. data/lib/steep/project/dsl.rb +268 -0
  96. data/lib/steep/project/group.rb +31 -0
  97. data/lib/steep/project/options.rb +63 -0
  98. data/lib/steep/project/pattern.rb +59 -0
  99. data/lib/steep/project/target.rb +92 -0
  100. data/lib/steep/project.rb +78 -0
  101. data/lib/steep/rake_task.rb +132 -0
  102. data/lib/steep/range_extension.rb +29 -0
  103. data/lib/steep/server/base_worker.rb +97 -0
  104. data/lib/steep/server/change_buffer.rb +73 -0
  105. data/lib/steep/server/custom_methods.rb +77 -0
  106. data/lib/steep/server/delay_queue.rb +45 -0
  107. data/lib/steep/server/interaction_worker.rb +492 -0
  108. data/lib/steep/server/lsp_formatter.rb +455 -0
  109. data/lib/steep/server/master.rb +912 -0
  110. data/lib/steep/server/target_group_files.rb +205 -0
  111. data/lib/steep/server/type_check_controller.rb +366 -0
  112. data/lib/steep/server/type_check_worker.rb +303 -0
  113. data/lib/steep/server/work_done_progress.rb +64 -0
  114. data/lib/steep/server/worker_process.rb +176 -0
  115. data/lib/steep/services/completion_provider.rb +802 -0
  116. data/lib/steep/services/content_change.rb +61 -0
  117. data/lib/steep/services/file_loader.rb +74 -0
  118. data/lib/steep/services/goto_service.rb +441 -0
  119. data/lib/steep/services/hover_provider/rbs.rb +88 -0
  120. data/lib/steep/services/hover_provider/ruby.rb +221 -0
  121. data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
  122. data/lib/steep/services/path_assignment.rb +46 -0
  123. data/lib/steep/services/signature_help_provider.rb +202 -0
  124. data/lib/steep/services/signature_service.rb +428 -0
  125. data/lib/steep/services/stats_calculator.rb +68 -0
  126. data/lib/steep/services/type_check_service.rb +394 -0
  127. data/lib/steep/services/type_name_completion.rb +236 -0
  128. data/lib/steep/signature/validator.rb +651 -0
  129. data/lib/steep/source/ignore_ranges.rb +69 -0
  130. data/lib/steep/source.rb +691 -0
  131. data/lib/steep/subtyping/cache.rb +30 -0
  132. data/lib/steep/subtyping/check.rb +1113 -0
  133. data/lib/steep/subtyping/constraints.rb +341 -0
  134. data/lib/steep/subtyping/relation.rb +101 -0
  135. data/lib/steep/subtyping/result.rb +324 -0
  136. data/lib/steep/subtyping/variable_variance.rb +89 -0
  137. data/lib/steep/test.rb +9 -0
  138. data/lib/steep/thread_waiter.rb +43 -0
  139. data/lib/steep/type_construction.rb +5183 -0
  140. data/lib/steep/type_inference/block_params.rb +416 -0
  141. data/lib/steep/type_inference/case_when.rb +303 -0
  142. data/lib/steep/type_inference/constant_env.rb +56 -0
  143. data/lib/steep/type_inference/context.rb +195 -0
  144. data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
  145. data/lib/steep/type_inference/method_call.rb +193 -0
  146. data/lib/steep/type_inference/method_params.rb +531 -0
  147. data/lib/steep/type_inference/multiple_assignment.rb +194 -0
  148. data/lib/steep/type_inference/send_args.rb +712 -0
  149. data/lib/steep/type_inference/type_env.rb +341 -0
  150. data/lib/steep/type_inference/type_env_builder.rb +138 -0
  151. data/lib/steep/typing.rb +321 -0
  152. data/lib/steep/version.rb +3 -0
  153. data/lib/steep.rb +369 -0
  154. data/manual/annotations.md +181 -0
  155. data/manual/ignore.md +20 -0
  156. data/manual/ruby-diagnostics.md +1879 -0
  157. data/sample/Steepfile +22 -0
  158. data/sample/lib/conference.rb +49 -0
  159. data/sample/lib/length.rb +35 -0
  160. data/sample/sig/conference.rbs +42 -0
  161. data/sample/sig/generics.rbs +15 -0
  162. data/sample/sig/length.rbs +34 -0
  163. data/steep-activesupport-4.gemspec +55 -0
  164. metadata +437 -0
@@ -0,0 +1,195 @@
1
+ # nil and Optional Types
2
+
3
+ `nil`s have been the most common source of the errors you see when testing and after the deployment of your apps.
4
+
5
+ ```
6
+ NoMethodError (undefined method `save!' for nil:NilClass)
7
+ ```
8
+
9
+ Steep/RBS provides *optional types* to help you identify the problems while you are coding.
10
+
11
+ ## Preventing `nil` problems
12
+
13
+ Technically, there is only one way to prevent `nil` problems – test everytime if the value is `nil` before calling methods with the receiver.
14
+
15
+ ```rb
16
+ if account
17
+ account.save!
18
+ end
19
+ ```
20
+
21
+ Using the `if` statement is the most popular way to ensure the value is not `nil`. But you can do it with safe-navigation-operators, case-when (or case-in), and `#try` method.
22
+
23
+ ```rb
24
+ account&.save!
25
+
26
+ case account
27
+ when Account
28
+ account.save!
29
+ end
30
+
31
+ account.try {|account| account.save! }
32
+ ```
33
+
34
+ It's simple, but not easy to do in your code.
35
+
36
+ **You may forget testing.** This happens easily. You don't notice that you forget until you deploy the code into production and it crashes after loading an account that is old and satisfies complicated conditions that leads it to be `nil`.
37
+
38
+ **You may add redundant guards.** This won't get your app crash, but it will make understanding your code more difficult. It adds unnecessary noise that tells your teammates this can be `nil` in some place, and results in another redundant guard.
39
+
40
+ The `nil` problems can be solved by a tool that tells you:
41
+
42
+ * If the value can be `nil` or not, and
43
+ * You forget testing the value before using the value
44
+
45
+ RBS has a language construct to do this called optional types and Steep implements analysis to let you know if you forget testing the value.
46
+
47
+ ## Optional types
48
+
49
+ Optional types in RBS are denoted with a suffix `?` – Type?. It means the value of the type may be `nil`.
50
+
51
+ ```
52
+ Integer? # Integer or nil
53
+ Array[Account]? # An array of Account or nil
54
+ ```
55
+
56
+ Note that optional types can be included in other types as:
57
+
58
+ ```
59
+ Array[Account?]
60
+ ```
61
+
62
+ The value of the type above is always an array, but the element may be `nil`.
63
+
64
+ In other words, a non optional type in RBS means the value cannot be `nil`.
65
+
66
+ ```
67
+ Integer # Integer, cannot be nil
68
+ Array[Account] # An array, cannot be nil
69
+ ```
70
+
71
+ Let's see how Steep reports errors on optional and non-optional types.
72
+
73
+ ```rb
74
+ account = Account.find(1)
75
+ account.save!
76
+ ```
77
+
78
+ Assume the type of `account` is `Account` (non optional type), the code type checks successfully. There is no chance to be `nil` here. The `save!` method call never results in a `NoMethodError`.
79
+
80
+ ```rb
81
+ account = Account.find_by(email: "soutaro@squareup.com")
82
+ account.save!
83
+ ```
84
+
85
+ Steep reports a `NoMethod` error on the `save!` call. Because the value of the `account` may be `nil`, depending on the actual records in the `accounts` table. You cannot call the `save!` method without checking if the `account` is `nil`.
86
+
87
+ You cannot assign `nil` to a local variable with non-optional types.
88
+
89
+ ```rb
90
+ # @type var account: Account
91
+
92
+ account = nil
93
+ account = Account.find_by(email: "soutaro@squareup.com")
94
+ ```
95
+
96
+ Because the type of `account` is declared Account, non-optional type, it cannot be `nil`. And Steep detects a type error if you try to assign `nil`. Same for assigning an optional type value at the last line.
97
+
98
+ # Unwrapping optional types
99
+
100
+ There are several ways to unwrap optional types. The most common one is using if.
101
+
102
+ ```rb
103
+ account = Account.find_by(id: 1)
104
+ if account
105
+ account.save!
106
+ end
107
+ ```
108
+
109
+ The *if* statement tests if `account` is `nil`. Inside the then clause, `account` cannot be `nil`. Then Steep type checks the code.
110
+
111
+ This works for *else* clause of *unless*.
112
+
113
+ ```rb
114
+ account = Account.find_by(id: 1)
115
+ unless account
116
+ # Do something
117
+ else
118
+ account.save!
119
+ end
120
+ ```
121
+
122
+ This also type checks successfully.
123
+
124
+ Steep supports `nil?` predicate too.
125
+
126
+ ```rb
127
+ unless (account = Account.find_by(id: 1)).nil?
128
+ account.save!
129
+ end
130
+ ```
131
+
132
+ This assumes the `Account` class doesn't have a custom `nil?` method, but keeps the built-in `nil?` or equivalent.
133
+
134
+ The last one is using safe-nevigation-navigator. It checks if the receiver is `nil` and calls the method if it is not. Otherwise just evaluates to `nil`.
135
+
136
+ ```rb
137
+ account = Account.find_by(id: 1)
138
+ account&.save!
139
+ ```
140
+
141
+ This is a shorthand for the case you don't do any error handling case if it is `nil`.
142
+
143
+ ## What should I do for the case of `nil`?
144
+
145
+ There is no universal answer for this question. You may just stop the execution of the method by returning. You may want to insert a new account to ensure the record exists. Raising an exception with a detailed error message will help troubleshooting.
146
+
147
+ It depends on what the program is expected to do. Steep just checks if accessing `nil` may happen or not. The developers only know how to handle the `nil` cases.
148
+
149
+ # Handling unwanted `nil`s
150
+
151
+ When you start using Steep, you may see many unwanted `nil`s. This typically happens when you want to use Array methods, like `first` or `sample`.
152
+
153
+ ```rb
154
+ account = accounts.first
155
+ account.save!
156
+ ```
157
+
158
+ It returns `nil` if the array is empty. Steep cannot detect if the array is empty or not, and it conservatively assumes the return value of the methods may be `nil`. While you know the `account` array is not empty, Steep infer the `first` method may return `nil`.
159
+
160
+ This is one of the most frequently seen sources of unwanted `nil`s.
161
+
162
+ ## Raising an error
163
+
164
+ In this case, you have to add an extra code to let Steep unwrap it.
165
+
166
+ ```rb
167
+ account = accounts.first or raise
168
+ account.save!
169
+ ```
170
+
171
+ My recommendation is to raise an exception, `|| raise` or `or raise`. It raises an exception in the case of `nil`, and Steep unwraps the type of the `account` variable.
172
+
173
+ Exceptions are better than other control flow operators – `return`/`break`/`next`. It doesn't affect the control flow until it actually happens during execution, and the type checking result other than the unwrapping is changed.
174
+
175
+ An `#raise` call without argument is my favorite. It's short. It's uncommon in the Ruby code and it can tell the readers that something unexpected is happening.
176
+
177
+ But of course, you can add some message:
178
+
179
+ ```rb
180
+ account = accounts.first or raise("accounts cannot be empty")
181
+ account.save!
182
+ ```
183
+
184
+ ## Type assertions
185
+
186
+ You can also use a type assertion, that is introduced in Steep 1.3.
187
+
188
+ ```rb
189
+ account = accounts.first #: Account
190
+ account.save!
191
+ ```
192
+
193
+ It tells Steep that the right hand side of the assignment is `Account`. That overwrites the type checking algorithm, and the developer is responsible for making sure the value cannot be `nil`.
194
+
195
+ Note: Nothing happens during the execution. It just works for Steep and Ruby doesn't do any extra type checking on it. I recommend using the `or raise` idiom for most of the cases.
@@ -0,0 +1,199 @@
1
+ module Steep
2
+ class AnnotationParser
3
+ VAR_NAME = /[a-z][A-Za-z0-9_]*/
4
+ METHOD_NAME = Regexp.union(
5
+ /[^.:\s]+/
6
+ )
7
+ CONST_NAME = Regexp.union(
8
+ /(::)?([A-Z][A-Za-z0-9_]*::)*[A-Z][A-Za-z0-9_]*/
9
+ )
10
+ DYNAMIC_NAME = /(self\??\.)?#{METHOD_NAME}/
11
+ IVAR_NAME = /@[^:\s]+/
12
+
13
+ attr_reader :factory
14
+
15
+ def initialize(factory:)
16
+ @factory = factory
17
+ end
18
+
19
+ class SyntaxError < StandardError
20
+ attr_reader :source
21
+ attr_reader :location
22
+
23
+ def initialize(source:, location:, exn: nil, message: nil)
24
+ @source = source
25
+ @location = location
26
+
27
+ if exn
28
+ message =
29
+ case exn
30
+ when RBS::ParsingError
31
+ Diagnostic::Signature::SyntaxError.parser_syntax_error_message(exn)
32
+ else
33
+ exn.message
34
+ end
35
+ end
36
+
37
+ super message
38
+ end
39
+ end
40
+
41
+ TYPE = /(?<type>.*)/
42
+ COLON = /\s*:\s*/
43
+
44
+ PARAM = /[A-Z][A-Za-z0-9_]*/
45
+ TYPE_PARAMS = /(\[(?<params>#{PARAM}(,\s*#{PARAM})*)\])?/
46
+
47
+ def parse_type(match, name = :type, location:)
48
+ string = match[name] or raise
49
+ st, en = match.offset(name)
50
+ st or raise
51
+ en or raise
52
+ loc = RBS::Location.new(location.buffer, location.start_pos + st, location.start_pos + en)
53
+
54
+ type =
55
+ begin
56
+ RBS::Parser.parse_type(string)
57
+ rescue RBS::ParsingError => exn
58
+ raise SyntaxError.new(source: string, location: loc, exn: exn)
59
+ end or raise
60
+
61
+ unless (type.location || raise).source == string.strip
62
+ raise SyntaxError.new(source: string, location: loc, message: "Failed to parse a type in annotation")
63
+ end
64
+
65
+ factory.type(type)
66
+ end
67
+
68
+ def keyword_subject_type(keyword, name)
69
+ /@type\s+#{keyword}\s+(?<name>#{name})#{COLON}#{TYPE}/
70
+ end
71
+
72
+ def keyword_and_type(keyword)
73
+ /@type\s+#{keyword}#{COLON}#{TYPE}/
74
+ end
75
+
76
+ def parse(src, location:)
77
+ case src
78
+ when keyword_subject_type("var", VAR_NAME)
79
+ Regexp.last_match.yield_self do |match|
80
+ match or raise
81
+ name = match[:name] or raise
82
+
83
+ AST::Annotation::VarType.new(name: name.to_sym,
84
+ type: parse_type(match, location: location),
85
+ location: location)
86
+ end
87
+
88
+ when keyword_subject_type("method", METHOD_NAME)
89
+ Regexp.last_match.yield_self do |match|
90
+ match or raise
91
+ name = match[:name] or raise
92
+ type = match[:type] or raise
93
+
94
+ method_type = factory.method_type(RBS::Parser.parse_method_type(type) || raise)
95
+
96
+ AST::Annotation::MethodType.new(name: name.to_sym,
97
+ type: method_type,
98
+ location: location)
99
+ end
100
+
101
+ when keyword_subject_type("const", CONST_NAME)
102
+ Regexp.last_match.yield_self do |match|
103
+ match or raise
104
+ name = match[:name] or raise
105
+ type = parse_type(match, location: location)
106
+
107
+ AST::Annotation::ConstType.new(name: RBS::TypeName.parse(name), type: type, location: location)
108
+ end
109
+
110
+ when keyword_subject_type("ivar", IVAR_NAME)
111
+ Regexp.last_match.yield_self do |match|
112
+ match or raise
113
+ name = match[:name] or raise
114
+ type = parse_type(match, location: location)
115
+
116
+ AST::Annotation::IvarType.new(name: name.to_sym,
117
+ type: type,
118
+ location: location)
119
+ end
120
+
121
+ when keyword_and_type("return")
122
+ Regexp.last_match.yield_self do |match|
123
+ match or raise
124
+ type = parse_type(match, location: location)
125
+ AST::Annotation::ReturnType.new(type: type, location: location)
126
+ end
127
+
128
+ when keyword_and_type("block")
129
+ Regexp.last_match.yield_self do |match|
130
+ match or raise
131
+ type = parse_type(match, location: location)
132
+ AST::Annotation::BlockType.new(type: type, location: location)
133
+ end
134
+
135
+ when keyword_and_type("self")
136
+ Regexp.last_match.yield_self do |match|
137
+ match or raise
138
+ type = parse_type(match, location: location)
139
+ AST::Annotation::SelfType.new(type: type, location: location)
140
+ end
141
+
142
+ when keyword_and_type("instance")
143
+ Regexp.last_match.yield_self do |match|
144
+ match or raise
145
+ type = parse_type(match, location: location)
146
+ AST::Annotation::InstanceType.new(type: type, location: location)
147
+ end
148
+
149
+ when keyword_and_type("module")
150
+ Regexp.last_match.yield_self do |match|
151
+ match or raise
152
+ type = parse_type(match, location: location)
153
+ AST::Annotation::ModuleType.new(type: type, location: location)
154
+ end
155
+
156
+ when keyword_and_type("break")
157
+ Regexp.last_match.yield_self do |match|
158
+ match or raise
159
+ type = parse_type(match, location: location)
160
+
161
+ AST::Annotation::BreakType.new(type: type, location: location)
162
+ end
163
+
164
+ when /@dynamic\s+(?<names>(#{DYNAMIC_NAME}\s*,\s*)*#{DYNAMIC_NAME})/
165
+ Regexp.last_match.yield_self do |match|
166
+ match or raise
167
+ names = (match[:names] || raise).split(/\s*,\s*/)
168
+
169
+ AST::Annotation::Dynamic.new(
170
+ names: names.map {|name|
171
+ case
172
+ when name.delete_prefix!("self.")
173
+ AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module)
174
+ when name.delete_prefix!("self?.")
175
+ AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module_instance)
176
+ else
177
+ AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :instance)
178
+ end
179
+ },
180
+ location: location
181
+ )
182
+ end
183
+
184
+ when /@implements\s+(?<name>#{CONST_NAME})#{TYPE_PARAMS}$/
185
+ Regexp.last_match.yield_self do |match|
186
+ match or raise
187
+ type_name = RBS::TypeName.parse(match[:name] || raise)
188
+ params = match[:params]&.yield_self {|params| params.split(/,/).map {|param| param.strip.to_sym } } || []
189
+
190
+ name = AST::Annotation::Implements::Module.new(name: type_name, args: params)
191
+ AST::Annotation::Implements.new(name: name, location: location)
192
+ end
193
+ end
194
+
195
+ rescue RBS::ParsingError => exn
196
+ raise SyntaxError.new(source: src, location: location, exn: exn)
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,172 @@
1
+ module Steep
2
+ module AST
3
+ module Annotation
4
+ class Collection
5
+ attr_reader :annotations
6
+ attr_reader :factory
7
+ attr_reader :context
8
+
9
+ attr_reader :var_type_annotations
10
+ attr_reader :const_type_annotations
11
+ attr_reader :ivar_type_annotations
12
+ attr_reader :method_type_annotations
13
+ attr_reader :block_type_annotation
14
+ attr_reader :return_type_annotation
15
+ attr_reader :self_type_annotation
16
+ attr_reader :instance_type_annotation
17
+ attr_reader :module_type_annotation
18
+ attr_reader :implement_module_annotation
19
+ attr_reader :dynamic_annotations
20
+ attr_reader :break_type_annotation
21
+
22
+ def initialize(annotations:, factory:, context:)
23
+ @annotations = annotations
24
+ @factory = factory
25
+ @context = context
26
+
27
+ @var_type_annotations = {}
28
+ @method_type_annotations = {}
29
+ @const_type_annotations = {}
30
+ @ivar_type_annotations = {}
31
+ @dynamic_annotations = []
32
+
33
+ annotations.each do |annotation|
34
+ case annotation
35
+ when VarType
36
+ var_type_annotations[annotation.name] = annotation
37
+ when MethodType
38
+ method_type_annotations[annotation.name] = annotation
39
+ when BlockType
40
+ @block_type_annotation = annotation
41
+ when ReturnType
42
+ @return_type_annotation = annotation
43
+ when SelfType
44
+ @self_type_annotation = annotation
45
+ when ConstType
46
+ @const_type_annotations[annotation.name] = annotation
47
+ when InstanceType
48
+ @instance_type_annotation = annotation
49
+ when ModuleType
50
+ @module_type_annotation = annotation
51
+ when Implements
52
+ @implement_module_annotation = annotation
53
+ when IvarType
54
+ @ivar_type_annotations[annotation.name] = annotation
55
+ when Dynamic
56
+ @dynamic_annotations << annotation
57
+ when BreakType
58
+ @break_type_annotation = annotation
59
+ else
60
+ raise "Unexpected annotation: #{annotation.inspect}"
61
+ end
62
+ end
63
+ end
64
+
65
+ def absolute_type(type)
66
+ if type
67
+ factory.absolute_type(type, context: context)
68
+ end
69
+ end
70
+
71
+ def var_type(lvar: nil, ivar: nil, const: nil)
72
+ case
73
+ when lvar
74
+ absolute_type(var_type_annotations[lvar]&.type)
75
+ when ivar
76
+ absolute_type(ivar_type_annotations[ivar]&.type)
77
+ when const
78
+ absolute_type(const_type_annotations[const]&.type)
79
+ end
80
+ end
81
+
82
+ def method_type(name)
83
+ if (a = method_type_annotations[name])
84
+ a.type.map_type {|type| absolute_type(type) }
85
+ end
86
+ end
87
+
88
+ def block_type
89
+ absolute_type(block_type_annotation&.type)
90
+ end
91
+
92
+ def return_type
93
+ absolute_type(return_type_annotation&.type)
94
+ end
95
+
96
+ def self_type
97
+ absolute_type(self_type_annotation&.type)
98
+ end
99
+
100
+ def instance_type
101
+ absolute_type(instance_type_annotation&.type)
102
+ end
103
+
104
+ def module_type
105
+ absolute_type(module_type_annotation&.type)
106
+ end
107
+
108
+ def break_type
109
+ absolute_type(break_type_annotation&.type)
110
+ end
111
+
112
+ def lvar_types
113
+ var_type_annotations.each_key.with_object({}) do |name, hash| #$ Hash[Symbol, Types::t]
114
+ hash[name] = var_type(lvar: name) || raise
115
+ end
116
+ end
117
+
118
+ def ivar_types
119
+ ivar_type_annotations.each_key.with_object({}) do |name, hash| #$ Hash[Symbol, Types::t]
120
+ hash[name] = var_type(ivar: name) || raise
121
+ end
122
+ end
123
+
124
+ def const_types
125
+ const_type_annotations.each_key.with_object({}) do |name, hash| #$ Hash[RBS::TypeName, Types::t]
126
+ hash[name] = var_type(const: name) || raise
127
+ end
128
+ end
129
+
130
+ def instance_dynamics
131
+ dynamic_annotations.flat_map do |annot|
132
+ annot.names.select(&:instance_method?).map(&:name)
133
+ end
134
+ end
135
+
136
+ def module_dynamics
137
+ dynamic_annotations.flat_map do |annot|
138
+ annot.names.select(&:module_method?).map(&:name)
139
+ end
140
+ end
141
+
142
+ def merge_block_annotations(annotations)
143
+ if annotations.context != context || annotations.factory != factory
144
+ raise "Cannot merge another annotation: self=#{self}, other=#{annotations}"
145
+ end
146
+
147
+ retained_annotations = self.annotations.reject do |annotation|
148
+ annotation.is_a?(BlockType) || annotation.is_a?(BreakType)
149
+ end
150
+
151
+ self.class.new(
152
+ annotations: retained_annotations + annotations.annotations,
153
+ factory: factory,
154
+ context: context
155
+ )
156
+ end
157
+
158
+ def any?(&block)
159
+ annotations.any?(&block)
160
+ end
161
+
162
+ def size
163
+ annotations.size
164
+ end
165
+
166
+ def include?(obj)
167
+ annotations.include?(obj)
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,137 @@
1
+ module Steep
2
+ module AST
3
+ module Annotation
4
+ module Located
5
+ attr_reader :location
6
+
7
+ def line
8
+ location&.start_line
9
+ end
10
+ end
11
+
12
+ class Named
13
+ include Located
14
+
15
+ attr_reader :name
16
+ attr_reader :type
17
+
18
+ def initialize(name:, type:, location: nil)
19
+ @name = name
20
+ @type = type
21
+ @location = location
22
+ end
23
+
24
+ def ==(other)
25
+ other.is_a?(self.class) &&
26
+ other.name == name &&
27
+ other.type == type
28
+ end
29
+ end
30
+
31
+ class Typed
32
+ include Located
33
+
34
+ attr_reader :type
35
+
36
+ def initialize(type:, location: nil)
37
+ @type = type
38
+ @location = location
39
+ end
40
+
41
+ def ==(other)
42
+ other.is_a?(self.class) &&
43
+ other.type == type
44
+ end
45
+ end
46
+
47
+ class ReturnType < Typed; end
48
+ class BlockType < Typed; end
49
+ class SelfType < Typed; end
50
+ class InstanceType < Typed; end
51
+ class ModuleType < Typed; end
52
+ class BreakType < Typed; end
53
+
54
+ class MethodType < Named; end
55
+ class VarType < Named; end
56
+ class ConstType < Named; end
57
+ class IvarType < Named; end
58
+
59
+ class Implements
60
+ class Module
61
+ attr_reader :name
62
+ attr_reader :args
63
+
64
+ def initialize(name:, args:)
65
+ @name = name
66
+ @args = args
67
+ end
68
+
69
+ def ==(other)
70
+ other.is_a?(Module) && other.name == name && other.args == args
71
+ end
72
+
73
+ alias eql? ==
74
+
75
+ def hash
76
+ self.class.hash ^ name.hash ^ args.hash
77
+ end
78
+ end
79
+
80
+ include Located
81
+
82
+ attr_reader :name
83
+
84
+ def initialize(name:, location: nil)
85
+ @location = location
86
+ @name = name
87
+ end
88
+
89
+ def ==(other)
90
+ other.is_a?(Implements) && other.name == name
91
+ end
92
+ end
93
+
94
+ class Dynamic
95
+ class Name
96
+ attr_reader :kind
97
+ attr_reader :name
98
+ attr_reader :location
99
+
100
+ def initialize(name:, kind:, location: nil)
101
+ @name = name
102
+ @kind = kind
103
+ @location = location
104
+ end
105
+
106
+ def instance_method?
107
+ kind == :instance || kind == :module_instance
108
+ end
109
+
110
+ def module_method?
111
+ kind == :module || kind == :module_instance
112
+ end
113
+
114
+ def ==(other)
115
+ other.is_a?(Name) &&
116
+ other.name == name &&
117
+ other.kind == kind
118
+ end
119
+ end
120
+
121
+ include Located
122
+
123
+ attr_reader :names
124
+
125
+ def initialize(names:, location: nil)
126
+ @location = location
127
+ @names = names
128
+ end
129
+
130
+ def ==(other)
131
+ other.is_a?(Dynamic) &&
132
+ other.names == names
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end