steep 0.43.0 → 0.45.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/ruby.yml +3 -2
  4. data/.gitignore +0 -1
  5. data/CHANGELOG.md +30 -0
  6. data/Gemfile +0 -1
  7. data/Gemfile.lock +77 -0
  8. data/bin/output_test.rb +8 -2
  9. data/lib/steep.rb +4 -1
  10. data/lib/steep/ast/builtin.rb +7 -1
  11. data/lib/steep/ast/types/factory.rb +19 -25
  12. data/lib/steep/diagnostic/ruby.rb +137 -60
  13. data/lib/steep/diagnostic/signature.rb +34 -0
  14. data/lib/steep/equatable.rb +21 -0
  15. data/lib/steep/index/source_index.rb +55 -5
  16. data/lib/steep/interface/block.rb +4 -0
  17. data/lib/steep/interface/function.rb +798 -579
  18. data/lib/steep/server/interaction_worker.rb +239 -20
  19. data/lib/steep/server/master.rb +40 -19
  20. data/lib/steep/server/type_check_worker.rb +68 -0
  21. data/lib/steep/services/file_loader.rb +26 -19
  22. data/lib/steep/services/goto_service.rb +322 -0
  23. data/lib/steep/services/hover_content.rb +131 -79
  24. data/lib/steep/services/type_check_service.rb +25 -0
  25. data/lib/steep/source.rb +7 -10
  26. data/lib/steep/type_construction.rb +496 -518
  27. data/lib/steep/type_inference/block_params.rb +2 -5
  28. data/lib/steep/type_inference/method_params.rb +483 -0
  29. data/lib/steep/type_inference/send_args.rb +610 -128
  30. data/lib/steep/typing.rb +46 -21
  31. data/lib/steep/version.rb +1 -1
  32. data/sig/steep/type_inference/send_args.rbs +42 -0
  33. data/smoke/array/test_expectations.yml +3 -3
  34. data/smoke/block/c.rb +0 -1
  35. data/smoke/class/test_expectations.yml +12 -15
  36. data/smoke/const/test_expectations.yml +0 -10
  37. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  38. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  39. data/smoke/diagnostics-ruby-unsat/Steepfile +5 -0
  40. data/smoke/diagnostics-ruby-unsat/a.rbs +3 -0
  41. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +27 -0
  42. data/smoke/{diagnostics → diagnostics-ruby-unsat}/unsatisfiable_constraint.rb +0 -1
  43. data/smoke/diagnostics/a.rbs +0 -4
  44. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  45. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  46. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  47. data/smoke/diagnostics/test_expectations.yml +108 -57
  48. data/smoke/ensure/test_expectations.yml +3 -3
  49. data/smoke/enumerator/test_expectations.yml +1 -1
  50. data/smoke/literal/test_expectations.yml +2 -2
  51. data/smoke/method/test_expectations.yml +11 -10
  52. data/smoke/regression/issue_372.rb +8 -0
  53. data/smoke/regression/issue_372.rbs +4 -0
  54. data/smoke/regression/test_expectations.yml +0 -12
  55. data/smoke/rescue/test_expectations.yml +3 -3
  56. data/smoke/toplevel/test_expectations.yml +3 -3
  57. data/smoke/tsort/test_expectations.yml +2 -2
  58. data/steep.gemspec +2 -2
  59. metadata +24 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0568c96d6a39d5c6db14e2259b5cdf3cd39bfaf5f2a4368ce060de4c803c0c96'
4
- data.tar.gz: d820c5f0c0d6394893a907999d4fdbd0f3a90b4821380c20598e9251d29852e6
3
+ metadata.gz: '080cf1692f90640baacc712908524e2daf36039ea992eb3424de7094c49f6778'
4
+ data.tar.gz: dd9f8837ec50e625ce9a0814fcbef03191bf484debd33034f9d00819115fef81
5
5
  SHA512:
6
- metadata.gz: 8f4f17d938d9f7f462f3db8aff1e48f8bf4118ab66bf40b35abd9b38ea3585014a0d8d372277e5589e9697043946b0e646193e4d7413c55fcf490d6065494203
7
- data.tar.gz: d7ece354f2e50faa86b65ed4a62c14f97dc74550fb81326142b8932f8c58deb2da40679a34e1869183e15ab4a90e939fb0041c44c63a5149b8234c82058481b0
6
+ metadata.gz: 67d26f6197f5b4fe53195c2202cdbbf556e2496fda4498151646c6d93a8455de0b75af66c35b16f32375fc239574600e33cc5f8caf92cc8f96a659ca0588b4da
7
+ data.tar.gz: 718c99448377245e8e0871af93c445d037aef4dbc727185bfd658f49672c08a11e8acc3807c7cb2a55814ffd32fb87642c9b37fc9f17143d4474a2810e2e4f3e
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "20:00"
8
+ open-pull-requests-limit: 10
@@ -12,8 +12,9 @@ jobs:
12
12
  strategy:
13
13
  matrix:
14
14
  container_tag:
15
- - 2.6.5-bionic
16
- - 2.7.0-bionic
15
+ - "2.6"
16
+ - "2.7"
17
+ - "3.0"
17
18
  task:
18
19
  - test
19
20
  - test:output
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.45.0 (2021-08-22)
6
+
7
+ * Fix error reporting on `RBS::MixinClassError` ([\#411](https://github.com/soutaro/steep/pull/411))
8
+ * Compact error reporting for method body type mismatch ([\#414](https://github.com/soutaro/steep/pull/414))
9
+ * Fix NoMethodError with csend/numblock ([\#412](https://github.com/soutaro/steep/pull/412))
10
+ * LSP completion for RBS files ([\#404](https://github.com/soutaro/steep/pull/404))
11
+ * Allow break without value from bot methods ([\#398](https://github.com/soutaro/steep/pull/398))
12
+ * Type check on lvar assignments ([\#390](https://github.com/soutaro/steep/pull/390))
13
+ * Assign different error code to break without value ([\#387](https://github.com/soutaro/steep/pull/387))
14
+ * Support Ruby3 Keyword Arguments ([\#386](https://github.com/soutaro/steep/pull/386))
15
+ * LSP hover for RBS files ([\#385](https://github.com/soutaro/steep/pull/385), [\#397](https://github.com/soutaro/steep/pull/397))
16
+ * Fix FileLoader to skip files not matching to the given pattern ([\#382](https://github.com/soutaro/steep/pull/382))
17
+ * Ruby3 support for numbered block parameters and end-less def ([\#381](https://github.com/soutaro/steep/pull/381))
18
+
19
+ ## 0.44.1 (2021-04-23)
20
+
21
+ * Disable goto declaration and goto type declaration (because they are not implemented) ([#377](https://github.com/soutaro/steep/pull/377))
22
+ * Fix goto from block calls ([#378](https://github.com/soutaro/steep/pull/378))
23
+
24
+ ## 0.44.0 (2021-04-22)
25
+
26
+ * Implement LSP go to definition/implementation ([#371](https://github.com/soutaro/steep/pull/371), [#375](https://github.com/soutaro/steep/pull/375))
27
+ * Fix typing on passing optional block ([#373](https://github.com/soutaro/steep/pull/373))
28
+ * Do not crash when completion request `context` is missing ([#370](https://github.com/soutaro/steep/pull/370))
29
+ * Update RBS ([#376](https://github.com/soutaro/steep/pull/376))
30
+
31
+ ## 0.43.1 (2021-04-01)
32
+
33
+ * Fix LSP `textDocument/didSave` notification handling ([#368](https://github.com/soutaro/steep/issues/368))
34
+
5
35
  ## 0.43.0 (2021-03-30)
6
36
 
7
37
  * LSP responsiveness improvements ([\#352](https://github.com/soutaro/steep/issues/352))
data/Gemfile CHANGED
@@ -9,6 +9,5 @@ gem "without_steep_types", path: "test/gems/without_steep_types"
9
9
  gem "rake"
10
10
  gem "minitest", "~> 5.0"
11
11
  gem "racc", "~> 1.4"
12
- gem "minitest-reporters"
13
12
  gem "minitest-hooks"
14
13
  gem "stackprof"
data/Gemfile.lock ADDED
@@ -0,0 +1,77 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ steep (0.45.0)
5
+ activesupport (>= 5.1)
6
+ language_server-protocol (>= 3.15, < 4.0)
7
+ listen (~> 3.0)
8
+ parallel (>= 1.0.0)
9
+ parser (>= 3.0)
10
+ rainbow (>= 2.2.2, < 4.0)
11
+ rbs (>= 1.2.0)
12
+ terminal-table (>= 2, < 4)
13
+
14
+ PATH
15
+ remote: test/gems/with_steep_types
16
+ specs:
17
+ with_steep_types (1.0.0)
18
+
19
+ PATH
20
+ remote: test/gems/without_steep_types
21
+ specs:
22
+ without_steep_types (1.0.0)
23
+
24
+ GEM
25
+ remote: https://rubygems.org/
26
+ specs:
27
+ activesupport (6.1.4.1)
28
+ concurrent-ruby (~> 1.0, >= 1.0.2)
29
+ i18n (>= 1.6, < 2)
30
+ minitest (>= 5.1)
31
+ tzinfo (~> 2.0)
32
+ zeitwerk (~> 2.3)
33
+ ast (2.4.2)
34
+ concurrent-ruby (1.1.9)
35
+ ffi (1.15.3)
36
+ i18n (1.8.10)
37
+ concurrent-ruby (~> 1.0)
38
+ language_server-protocol (3.16.0.1)
39
+ listen (3.7.0)
40
+ rb-fsevent (~> 0.10, >= 0.10.3)
41
+ rb-inotify (~> 0.9, >= 0.9.10)
42
+ minitest (5.14.4)
43
+ minitest-hooks (1.5.0)
44
+ minitest (> 5.3)
45
+ parallel (1.20.1)
46
+ parser (3.0.2.0)
47
+ ast (~> 2.4.1)
48
+ racc (1.5.2)
49
+ rainbow (3.0.0)
50
+ rake (13.0.6)
51
+ rb-fsevent (0.11.0)
52
+ rb-inotify (0.10.1)
53
+ ffi (~> 1.0)
54
+ rbs (1.5.1)
55
+ stackprof (0.2.17)
56
+ terminal-table (3.0.1)
57
+ unicode-display_width (>= 1.1.1, < 3)
58
+ tzinfo (2.0.4)
59
+ concurrent-ruby (~> 1.0)
60
+ unicode-display_width (2.0.0)
61
+ zeitwerk (2.4.2)
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ minitest (~> 5.0)
68
+ minitest-hooks
69
+ racc (~> 1.4)
70
+ rake
71
+ stackprof
72
+ steep!
73
+ with_steep_types!
74
+ without_steep_types!
75
+
76
+ BUNDLED WITH
77
+ 2.2.22
data/bin/output_test.rb CHANGED
@@ -16,6 +16,8 @@ end
16
16
 
17
17
  failed_tests = []
18
18
 
19
+ ALLOW_FAILURE = ["diagnostics-ruby-unsat"]
20
+
19
21
  test_dirs.each do |dir|
20
22
  puts "Running test #{dir}..."
21
23
 
@@ -30,8 +32,12 @@ test_dirs.each do |dir|
30
32
  output, status = Open3.capture2(*command, chdir: dir.to_s)
31
33
 
32
34
  unless status.success?
33
- failed_tests << dir.basename
34
- puts " Failed! 🤕"
35
+ unless ALLOW_FAILURE.include?(dir.basename.to_s)
36
+ failed_tests << dir.basename
37
+ puts " Failed! 🤕"
38
+ else
39
+ puts " Failed! 🤕 (ALLOW_FAILURE)"
40
+ end
35
41
  else
36
42
  puts " Succeed! 👍"
37
43
  end
data/lib/steep.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "steep/version"
2
2
 
3
3
  require "pathname"
4
- require "parser/ruby27"
4
+ require "parser/ruby30"
5
5
  require "active_support/core_ext/object/try"
6
6
  require "active_support/core_ext/string/inflections"
7
7
  require "logger"
@@ -21,6 +21,7 @@ require "terminal-table"
21
21
 
22
22
  require "rbs"
23
23
 
24
+ require "steep/equatable"
24
25
  require "steep/method_name"
25
26
  require "steep/ast/types/helper"
26
27
  require "steep/ast/types/any"
@@ -77,6 +78,7 @@ require "steep/type_inference/context"
77
78
  require "steep/type_inference/context_array"
78
79
  require "steep/type_inference/send_args"
79
80
  require "steep/type_inference/block_params"
81
+ require "steep/type_inference/method_params"
80
82
  require "steep/type_inference/constant_env"
81
83
  require "steep/type_inference/type_env"
82
84
  require "steep/type_inference/local_variable_type_env"
@@ -104,6 +106,7 @@ require "steep/services/hover_content"
104
106
  require "steep/services/completion_provider"
105
107
  require "steep/services/stats_calculator"
106
108
  require "steep/services/file_loader"
109
+ require "steep/services/goto_service"
107
110
 
108
111
  require "steep/project"
109
112
  require "steep/project/pattern"
@@ -10,8 +10,14 @@ module Steep
10
10
  @arity = arity
11
11
  end
12
12
 
13
- def instance_type(*args)
13
+ def instance_type(*args, fill_untyped: false)
14
+ if fill_untyped
15
+ (arity - args.size).times do
16
+ args << Builtin.any_type
17
+ end
18
+ end
14
19
  arity == args.size or raise "Mulformed instance type: name=#{module_name}, args=#{args}"
20
+
15
21
  Types::Name::Instance.new(name: module_name, args: args)
16
22
  end
17
23
 
@@ -194,7 +194,7 @@ module Steep
194
194
  end
195
195
 
196
196
  def params(type)
197
- Interface::Function::Params.new(
197
+ Interface::Function::Params.build(
198
198
  required: type.required_positionals.map {|param| type(param.type) },
199
199
  optional: type.optional_positionals.map {|param| type(param.type) },
200
200
  rest: type.rest_positionals&.yield_self {|param| type(param.type) },
@@ -621,12 +621,7 @@ module Steep
621
621
  Interface::MethodType.new(
622
622
  type_params: [],
623
623
  type: Interface::Function.new(
624
- params: Interface::Function::Params.new(required: [AST::Types::Literal.new(value: index)],
625
- optional: [],
626
- rest: nil,
627
- required_keywords: {},
628
- optional_keywords: {},
629
- rest_keywords: nil),
624
+ params: Interface::Function::Params.build(required: [AST::Types::Literal.new(value: index)]),
630
625
  return_type: elem_type,
631
626
  location: nil
632
627
  ),
@@ -643,12 +638,7 @@ module Steep
643
638
  Interface::MethodType.new(
644
639
  type_params: [],
645
640
  type: Interface::Function.new(
646
- params: Interface::Function::Params.new(required: [AST::Types::Literal.new(value: index), elem_type],
647
- optional: [],
648
- rest: nil,
649
- required_keywords: {},
650
- optional_keywords: {},
651
- rest_keywords: nil),
641
+ params: Interface::Function::Params.build(required: [AST::Types::Literal.new(value: index), elem_type]),
652
642
  return_type: elem_type,
653
643
  location: nil
654
644
  ),
@@ -712,12 +702,14 @@ module Steep
712
702
  Interface::MethodType.new(
713
703
  type_params: [],
714
704
  type: Interface::Function.new(
715
- params: Interface::Function::Params.new(required: [key_type],
716
- optional: [],
717
- rest: nil,
718
- required_keywords: {},
719
- optional_keywords: {},
720
- rest_keywords: nil),
705
+ params: Interface::Function::Params.build(
706
+ required: [key_type],
707
+ optional: [],
708
+ rest: nil,
709
+ required_keywords: {},
710
+ optional_keywords: {},
711
+ rest_keywords: nil
712
+ ),
721
713
  return_type: value_type,
722
714
  location: nil
723
715
  ),
@@ -735,12 +727,14 @@ module Steep
735
727
  Interface::MethodType.new(
736
728
  type_params: [],
737
729
  type: Interface::Function.new(
738
- params: Interface::Function::Params.new(required: [key_type, value_type],
739
- optional: [],
740
- rest: nil,
741
- required_keywords: {},
742
- optional_keywords: {},
743
- rest_keywords: nil),
730
+ params: Interface::Function::Params.build(
731
+ required: [key_type, value_type],
732
+ optional: [],
733
+ rest: nil,
734
+ required_keywords: {},
735
+ optional_keywords: {},
736
+ rest_keywords: nil
737
+ ),
744
738
  return_type: value_type,
745
739
  location: nil),
746
740
  block: nil,
@@ -71,46 +71,104 @@ module Steep
71
71
  end
72
72
  end
73
73
 
74
- class IncompatibleArguments < Base
74
+ class UnexpectedPositionalArgument < Base
75
75
  attr_reader :node
76
+ attr_reader :method_type
76
77
  attr_reader :method_name
77
- attr_reader :receiver_type
78
- attr_reader :method_types
79
78
 
80
- def initialize(node:, method_name:, receiver_type:, method_types:)
81
- location = case node.type
82
- when :send
83
- node.loc.selector
84
- when :block
85
- node.children[0].yield_self do |node|
86
- node.loc.selector
87
- end
88
- when :super
89
- node.loc.expression
90
- else
91
- Steep.logger.error { "Unexpected node given: #{node.type} (IncompatibleArguments#initialize)"}
92
- node.loc.expression
93
- end
94
- super(node: node, location: location)
95
- @receiver_type = receiver_type
96
- @method_types = method_types
79
+ def initialize(node:, method_name:, method_type:)
80
+ super(node: node)
97
81
  @method_name = method_name
82
+ @method_type = method_type
98
83
  end
99
84
 
100
85
  def header_line
101
- "Cannot find method `#{method_name}` of type `#{receiver_type}` with compatible arity"
86
+ "Unexpected positional argument"
102
87
  end
88
+ end
103
89
 
104
- def detail_lines
105
- StringIO.new.tap do |io|
106
- io.puts "Method types:"
107
- first_type, *rest_types = method_types
108
- defn = " def #{method_name}"
109
- io.puts "#{defn}: #{first_type}"
110
- rest_types.each do |method_type|
111
- io.puts "#{" " * defn.size}| #{method_type}"
112
- end
113
- end.string.chomp
90
+ class InsufficientPositionalArguments < Base
91
+ attr_reader :node
92
+ attr_reader :method_name
93
+ attr_reader :method_type
94
+
95
+ def initialize(node:, method_name:, method_type:)
96
+ send = case node.type
97
+ when :send, :csend
98
+ node
99
+ when :block, :numblock
100
+ node.children[0]
101
+ end
102
+
103
+ loc = if send
104
+ send.loc.selector.with(end_pos: send.loc.expression.end_pos)
105
+ else
106
+ node.loc.expression
107
+ end
108
+
109
+ super(node: node, location: loc)
110
+ @method_name = method_name
111
+ @method_type = method_type
112
+ end
113
+
114
+ def header_line
115
+ "More positional arguments are required"
116
+ end
117
+ end
118
+
119
+ class UnexpectedKeywordArgument < Base
120
+ attr_reader :node
121
+ attr_reader :method_name
122
+ attr_reader :method_type
123
+
124
+ def initialize(node:, method_name:, method_type:)
125
+ loc = case node.type
126
+ when :pair
127
+ node.children[0].location.expression
128
+ when :kwsplat
129
+ node.location.expression
130
+ else
131
+ raise
132
+ end
133
+ super(node: node, location: loc)
134
+ @method_name = method_name
135
+ @method_type = method_type
136
+ end
137
+
138
+ def header_line
139
+ "Unexpected keyword argument"
140
+ end
141
+ end
142
+
143
+ class InsufficientKeywordArguments < Base
144
+ attr_reader :node
145
+ attr_reader :method_name
146
+ attr_reader :method_type
147
+ attr_reader :missing_keywords
148
+
149
+ def initialize(node:, method_name:, method_type:, missing_keywords:)
150
+ send = case node.type
151
+ when :send, :csend
152
+ node
153
+ when :block, :numblock
154
+ node.children[0]
155
+ end
156
+
157
+ loc = if send
158
+ send.loc.selector.with(end_pos: send.loc.expression.end_pos)
159
+ else
160
+ node.loc.expression
161
+ end
162
+
163
+ super(node: node, location: loc)
164
+
165
+ @method_name = method_name
166
+ @method_type = method_type
167
+ @missing_keywords = missing_keywords
168
+ end
169
+
170
+ def header_line
171
+ "More keyword arguments are required: #{missing_keywords.join(", ")}"
114
172
  end
115
173
  end
116
174
 
@@ -292,6 +350,23 @@ module Steep
292
350
  end
293
351
  end
294
352
 
353
+ class ImplicitBreakValueMismatch < Base
354
+ attr_reader :jump_type
355
+ attr_reader :result
356
+
357
+ include ResultPrinter
358
+
359
+ def initialize(node:, jump_type:, result:)
360
+ super(node: node)
361
+ @jump_type = jump_type
362
+ @result = result
363
+ end
364
+
365
+ def header_line
366
+ "Breaking without a value may result an error because a value of type `#{jump_type}` is expected"
367
+ end
368
+ end
369
+
295
370
  class UnexpectedJump < Base
296
371
  def header_line
297
372
  "Cannot jump from here"
@@ -323,6 +398,36 @@ module Steep
323
398
  end
324
399
  end
325
400
 
401
+ class MethodParameterMismatch < Base
402
+ attr_reader :method_param
403
+ attr_reader :method_type
404
+
405
+ def initialize(method_param:, method_type:)
406
+ super(node: method_param.node)
407
+ @method_param = method_param
408
+ @method_type = method_type
409
+ end
410
+
411
+ def header_line
412
+ "The method parameter is incompatible with the declaration `#{method_type}`"
413
+ end
414
+ end
415
+
416
+ class DifferentMethodParameterKind < Base
417
+ attr_reader :method_param
418
+ attr_reader :method_type
419
+
420
+ def initialize(method_param:, method_type:)
421
+ super(node: method_param.node)
422
+ @method_param = method_param
423
+ @method_type = method_type
424
+ end
425
+
426
+ def header_line
427
+ "The method parameter has different kind from the declaration `#{method_type}`"
428
+ end
429
+ end
430
+
326
431
  class IncompatibleMethodTypeAnnotation < Base
327
432
  attr_reader :interface_method
328
433
  attr_reader :annotation_method
@@ -366,7 +471,7 @@ module Steep
366
471
  include ResultPrinter
367
472
 
368
473
  def initialize(node:, expected:, actual:, result:)
369
- super(node: node)
474
+ super(node: node, location: node.loc.name)
370
475
  @expected = expected
371
476
  @actual = actual
372
477
  @result = result
@@ -552,34 +657,6 @@ module Steep
552
657
  end
553
658
  end
554
659
 
555
- class UnexpectedKeyword < Base
556
- attr_reader :unexpected_keywords
557
-
558
- def initialize(node:, unexpected_keywords:)
559
- super(node: node)
560
- @unexpected_keywords = unexpected_keywords
561
- end
562
-
563
- def header_line
564
- keywords = unexpected_keywords.sort.map {|x| "`#{x}`" }
565
- "Cannot specify unexpected keyword arguments: #{keywords.join(", ")}"
566
- end
567
- end
568
-
569
- class MissingKeyword < Base
570
- attr_reader :missing_keywords
571
-
572
- def initialize(node:, missing_keywords:)
573
- super(node: node)
574
- @missing_keywords = missing_keywords
575
- end
576
-
577
- def header_line
578
- keywords = missing_keywords.sort.map {|x| "`#{x}`" }
579
- "Cannot omit required keywords: #{keywords.join(", ")}"
580
- end
581
- end
582
-
583
660
  class UnsupportedSyntax < Base
584
661
  attr_reader :message
585
662