did_you_mean 1.5.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ruby.yml +3 -3
  4. data/CHANGELOG.md +37 -2
  5. data/README.md +7 -15
  6. data/Rakefile +9 -2
  7. data/documentation/tree_spell_checker_api.md +1 -1
  8. data/lib/did_you_mean/core_ext/name_error.rb +43 -11
  9. data/lib/did_you_mean/formatter.rb +44 -0
  10. data/lib/did_you_mean/formatters/plain_formatter.rb +3 -32
  11. data/lib/did_you_mean/formatters/verbose_formatter.rb +5 -44
  12. data/lib/did_you_mean/spell_checker.rb +9 -9
  13. data/lib/did_you_mean/spell_checkers/method_name_checker.rb +3 -0
  14. data/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb +4 -1
  15. data/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb +20 -0
  16. data/lib/did_you_mean/spell_checkers/require_path_checker.rb +5 -1
  17. data/lib/did_you_mean/verbose.rb +2 -4
  18. data/lib/did_you_mean/version.rb +1 -1
  19. data/lib/did_you_mean.rb +51 -8
  20. data/test/core_ext/test_name_error_extension.rb +15 -8
  21. data/test/helper.rb +14 -0
  22. data/test/lib/core_assertions.rb +757 -0
  23. data/test/lib/envutil.rb +372 -0
  24. data/test/lib/find_executable.rb +22 -0
  25. data/test/lib/helper.rb +3 -0
  26. data/test/spell_checking/test_key_name_check.rb +7 -7
  27. data/test/spell_checking/test_method_name_check.rb +10 -10
  28. data/test/spell_checking/test_pattern_key_name_check.rb +20 -0
  29. data/test/spell_checking/test_require_path_check.rb +3 -3
  30. data/test/spell_checking/test_uncorrectable_name_check.rb +1 -1
  31. data/test/spell_checking/test_variable_name_check.rb +24 -12
  32. data/test/test_ractor_compatibility.rb +117 -0
  33. data/test/test_spell_checker.rb +1 -0
  34. data/test/tree_spell/test_change_word.rb +1 -1
  35. metadata +21 -8
  36. data/test/test_verbose_formatter.rb +0 -23
@@ -0,0 +1,372 @@
1
+ # -*- coding: us-ascii -*-
2
+ # frozen_string_literal: true
3
+ require "open3"
4
+ require "timeout"
5
+ require_relative "find_executable"
6
+ begin
7
+ require 'rbconfig'
8
+ rescue LoadError
9
+ end
10
+ begin
11
+ require "rbconfig/sizeof"
12
+ rescue LoadError
13
+ end
14
+
15
+ module EnvUtil
16
+ def rubybin
17
+ if ruby = ENV["RUBY"]
18
+ return ruby
19
+ end
20
+ ruby = "ruby"
21
+ exeext = RbConfig::CONFIG["EXEEXT"]
22
+ rubyexe = (ruby + exeext if exeext and !exeext.empty?)
23
+ 3.times do
24
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
25
+ return File.expand_path(ruby)
26
+ end
27
+ if rubyexe and File.exist? rubyexe and File.executable? rubyexe
28
+ return File.expand_path(rubyexe)
29
+ end
30
+ ruby = File.join("..", ruby)
31
+ end
32
+ if defined?(RbConfig.ruby)
33
+ RbConfig.ruby
34
+ else
35
+ "ruby"
36
+ end
37
+ end
38
+ module_function :rubybin
39
+
40
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
41
+
42
+ DEFAULT_SIGNALS = Signal.list
43
+ DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM
44
+
45
+ RUBYLIB = ENV["RUBYLIB"]
46
+
47
+ class << self
48
+ attr_accessor :timeout_scale
49
+ attr_reader :original_internal_encoding, :original_external_encoding,
50
+ :original_verbose, :original_warning
51
+
52
+ def capture_global_values
53
+ @original_internal_encoding = Encoding.default_internal
54
+ @original_external_encoding = Encoding.default_external
55
+ @original_verbose = $VERBOSE
56
+ @original_warning = defined?(Warning.[]) ? %i[deprecated experimental].to_h {|i| [i, Warning[i]]} : nil
57
+ end
58
+ end
59
+
60
+ def apply_timeout_scale(t)
61
+ if scale = EnvUtil.timeout_scale
62
+ t * scale
63
+ else
64
+ t
65
+ end
66
+ end
67
+ module_function :apply_timeout_scale
68
+
69
+ def timeout(sec, klass = nil, message = nil, &blk)
70
+ return yield(sec) if sec == nil or sec.zero?
71
+ sec = apply_timeout_scale(sec)
72
+ Timeout.timeout(sec, klass, message, &blk)
73
+ end
74
+ module_function :timeout
75
+
76
+ def terminate(pid, signal = :TERM, pgroup = nil, reprieve = 1)
77
+ reprieve = apply_timeout_scale(reprieve) if reprieve
78
+
79
+ signals = Array(signal).select do |sig|
80
+ DEFAULT_SIGNALS[sig.to_s] or
81
+ DEFAULT_SIGNALS[Signal.signame(sig)] rescue false
82
+ end
83
+ signals |= [:ABRT, :KILL]
84
+ case pgroup
85
+ when 0, true
86
+ pgroup = -pid
87
+ when nil, false
88
+ pgroup = pid
89
+ end
90
+
91
+ lldb = true if /darwin/ =~ RUBY_PLATFORM
92
+
93
+ while signal = signals.shift
94
+
95
+ if lldb and [:ABRT, :KILL].include?(signal)
96
+ lldb = false
97
+ # sudo -n: --non-interactive
98
+ # lldb -p: attach
99
+ # -o: run command
100
+ system(*%W[sudo -n lldb -p #{pid} --batch -o bt\ all -o call\ rb_vmdebug_stack_dump_all_threads() -o quit])
101
+ true
102
+ end
103
+
104
+ begin
105
+ Process.kill signal, pgroup
106
+ rescue Errno::EINVAL
107
+ next
108
+ rescue Errno::ESRCH
109
+ break
110
+ end
111
+ if signals.empty? or !reprieve
112
+ Process.wait(pid)
113
+ else
114
+ begin
115
+ Timeout.timeout(reprieve) {Process.wait(pid)}
116
+ rescue Timeout::Error
117
+ else
118
+ break
119
+ end
120
+ end
121
+ end
122
+ $?
123
+ end
124
+ module_function :terminate
125
+
126
+ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
127
+ encoding: nil, timeout: 10, reprieve: 1, timeout_error: Timeout::Error,
128
+ stdout_filter: nil, stderr_filter: nil, ios: nil,
129
+ signal: :TERM,
130
+ rubybin: EnvUtil.rubybin, precommand: nil,
131
+ **opt)
132
+ timeout = apply_timeout_scale(timeout)
133
+
134
+ in_c, in_p = IO.pipe
135
+ out_p, out_c = IO.pipe if capture_stdout
136
+ err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
137
+ opt[:in] = in_c
138
+ opt[:out] = out_c if capture_stdout
139
+ opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
140
+ if encoding
141
+ out_p.set_encoding(encoding) if out_p
142
+ err_p.set_encoding(encoding) if err_p
143
+ end
144
+ ios.each {|i, o = i|opt[i] = o} if ios
145
+
146
+ c = "C"
147
+ child_env = {}
148
+ LANG_ENVS.each {|lc| child_env[lc] = c}
149
+ if Array === args and Hash === args.first
150
+ child_env.update(args.shift)
151
+ end
152
+ if RUBYLIB and lib = child_env["RUBYLIB"]
153
+ child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR)
154
+ end
155
+
156
+ # remain env
157
+ %w(ASAN_OPTIONS RUBY_ON_BUG).each{|name|
158
+ child_env[name] = ENV[name] if ENV[name]
159
+ }
160
+
161
+ args = [args] if args.kind_of?(String)
162
+ pid = spawn(child_env, *precommand, rubybin, *args, opt)
163
+ in_c.close
164
+ out_c&.close
165
+ out_c = nil
166
+ err_c&.close
167
+ err_c = nil
168
+ if block_given?
169
+ return yield in_p, out_p, err_p, pid
170
+ else
171
+ th_stdout = Thread.new { out_p.read } if capture_stdout
172
+ th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
173
+ in_p.write stdin_data.to_str unless stdin_data.empty?
174
+ in_p.close
175
+ if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
176
+ timeout_error = nil
177
+ else
178
+ status = terminate(pid, signal, opt[:pgroup], reprieve)
179
+ terminated = Time.now
180
+ end
181
+ stdout = th_stdout.value if capture_stdout
182
+ stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
183
+ out_p.close if capture_stdout
184
+ err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
185
+ status ||= Process.wait2(pid)[1]
186
+ stdout = stdout_filter.call(stdout) if stdout_filter
187
+ stderr = stderr_filter.call(stderr) if stderr_filter
188
+ if timeout_error
189
+ bt = caller_locations
190
+ msg = "execution of #{bt.shift.label} expired timeout (#{timeout} sec)"
191
+ msg = failure_description(status, terminated, msg, [stdout, stderr].join("\n"))
192
+ raise timeout_error, msg, bt.map(&:to_s)
193
+ end
194
+ return stdout, stderr, status
195
+ end
196
+ ensure
197
+ [th_stdout, th_stderr].each do |th|
198
+ th.kill if th
199
+ end
200
+ [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
201
+ io&.close
202
+ end
203
+ [th_stdout, th_stderr].each do |th|
204
+ th.join if th
205
+ end
206
+ end
207
+ module_function :invoke_ruby
208
+
209
+ def verbose_warning
210
+ class << (stderr = "".dup)
211
+ alias write concat
212
+ def flush; end
213
+ end
214
+ stderr, $stderr = $stderr, stderr
215
+ $VERBOSE = true
216
+ yield stderr
217
+ return $stderr
218
+ ensure
219
+ stderr, $stderr = $stderr, stderr
220
+ $VERBOSE = EnvUtil.original_verbose
221
+ EnvUtil.original_warning&.each {|i, v| Warning[i] = v}
222
+ end
223
+ module_function :verbose_warning
224
+
225
+ def default_warning
226
+ $VERBOSE = false
227
+ yield
228
+ ensure
229
+ $VERBOSE = EnvUtil.original_verbose
230
+ end
231
+ module_function :default_warning
232
+
233
+ def suppress_warning
234
+ $VERBOSE = nil
235
+ yield
236
+ ensure
237
+ $VERBOSE = EnvUtil.original_verbose
238
+ end
239
+ module_function :suppress_warning
240
+
241
+ def under_gc_stress(stress = true)
242
+ stress, GC.stress = GC.stress, stress
243
+ yield
244
+ ensure
245
+ GC.stress = stress
246
+ end
247
+ module_function :under_gc_stress
248
+
249
+ def with_default_external(enc)
250
+ suppress_warning { Encoding.default_external = enc }
251
+ yield
252
+ ensure
253
+ suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding }
254
+ end
255
+ module_function :with_default_external
256
+
257
+ def with_default_internal(enc)
258
+ suppress_warning { Encoding.default_internal = enc }
259
+ yield
260
+ ensure
261
+ suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding }
262
+ end
263
+ module_function :with_default_internal
264
+
265
+ def labeled_module(name, &block)
266
+ Module.new do
267
+ singleton_class.class_eval {
268
+ define_method(:to_s) {name}
269
+ alias inspect to_s
270
+ alias name to_s
271
+ }
272
+ class_eval(&block) if block
273
+ end
274
+ end
275
+ module_function :labeled_module
276
+
277
+ def labeled_class(name, superclass = Object, &block)
278
+ Class.new(superclass) do
279
+ singleton_class.class_eval {
280
+ define_method(:to_s) {name}
281
+ alias inspect to_s
282
+ alias name to_s
283
+ }
284
+ class_eval(&block) if block
285
+ end
286
+ end
287
+ module_function :labeled_class
288
+
289
+ if /darwin/ =~ RUBY_PLATFORM
290
+ DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
291
+ DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
292
+ @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME']
293
+
294
+ def self.diagnostic_reports(signame, pid, now)
295
+ return unless %w[ABRT QUIT SEGV ILL TRAP].include?(signame)
296
+ cmd = File.basename(rubybin)
297
+ cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd
298
+ path = DIAGNOSTIC_REPORTS_PATH
299
+ timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
300
+ pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
301
+ first = true
302
+ 30.times do
303
+ first ? (first = false) : sleep(0.1)
304
+ Dir.glob(pat) do |name|
305
+ log = File.read(name) rescue next
306
+ if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
307
+ File.unlink(name)
308
+ File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
309
+ return log
310
+ end
311
+ end
312
+ end
313
+ nil
314
+ end
315
+ else
316
+ def self.diagnostic_reports(signame, pid, now)
317
+ end
318
+ end
319
+
320
+ def self.failure_description(status, now, message = "", out = "")
321
+ pid = status.pid
322
+ if signo = status.termsig
323
+ signame = Signal.signame(signo)
324
+ sigdesc = "signal #{signo}"
325
+ end
326
+ log = diagnostic_reports(signame, pid, now)
327
+ if signame
328
+ sigdesc = "SIG#{signame} (#{sigdesc})"
329
+ end
330
+ if status.coredump?
331
+ sigdesc = "#{sigdesc} (core dumped)"
332
+ end
333
+ full_message = ''.dup
334
+ message = message.call if Proc === message
335
+ if message and !message.empty?
336
+ full_message << message << "\n"
337
+ end
338
+ full_message << "pid #{pid}"
339
+ full_message << " exit #{status.exitstatus}" if status.exited?
340
+ full_message << " killed by #{sigdesc}" if sigdesc
341
+ if out and !out.empty?
342
+ full_message << "\n" << out.b.gsub(/^/, '| ')
343
+ full_message.sub!(/(?<!\n)\z/, "\n")
344
+ end
345
+ if log
346
+ full_message << "Diagnostic reports:\n" << log.b.gsub(/^/, '| ')
347
+ end
348
+ full_message
349
+ end
350
+
351
+ def self.gc_stress_to_class?
352
+ unless defined?(@gc_stress_to_class)
353
+ _, _, status = invoke_ruby(["-e""exit GC.respond_to?(:add_stress_to_class)"])
354
+ @gc_stress_to_class = status.success?
355
+ end
356
+ @gc_stress_to_class
357
+ end
358
+ end
359
+
360
+ if defined?(RbConfig)
361
+ module RbConfig
362
+ @ruby = EnvUtil.rubybin
363
+ class << self
364
+ undef ruby if method_defined?(:ruby)
365
+ attr_reader :ruby
366
+ end
367
+ dir = File.dirname(ruby)
368
+ CONFIG['bindir'] = dir
369
+ end
370
+ end
371
+
372
+ EnvUtil.capture_global_values
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require "rbconfig"
3
+
4
+ module EnvUtil
5
+ def find_executable(cmd, *args)
6
+ exts = RbConfig::CONFIG["EXECUTABLE_EXTS"].split | [RbConfig::CONFIG["EXEEXT"]]
7
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
8
+ next if path.empty?
9
+ path = File.join(path, cmd)
10
+ exts.each do |ext|
11
+ cmdline = [path + ext, *args]
12
+ begin
13
+ return cmdline if yield(IO.popen(cmdline, "r", err: [:child, :out], &:read))
14
+ rescue
15
+ next
16
+ end
17
+ end
18
+ end
19
+ nil
20
+ end
21
+ module_function :find_executable
22
+ end
@@ -0,0 +1,3 @@
1
+ require "test/unit"
2
+ require_relative "core_assertions"
3
+ Test::Unit::TestCase.include Test::Unit::CoreAssertions
@@ -8,11 +8,11 @@ class KeyNameCheckTest < Test::Unit::TestCase
8
8
 
9
9
  error = assert_raise(KeyError) { hash.fetch(:bax) }
10
10
  assert_correction ":bar", error.corrections
11
- assert_match "Did you mean? :bar", error.to_s
11
+ assert_match "Did you mean? :bar", get_message(error)
12
12
 
13
13
  error = assert_raise(KeyError) { hash.fetch("fooo") }
14
14
  assert_correction %("foo"), error.corrections
15
- assert_match %(Did you mean? "foo"), error.to_s
15
+ assert_match %(Did you mean? "foo"), get_message(error)
16
16
  end
17
17
 
18
18
  def test_corrects_hash_key_name_with_fetch_values
@@ -20,11 +20,11 @@ class KeyNameCheckTest < Test::Unit::TestCase
20
20
 
21
21
  error = assert_raise(KeyError) { hash.fetch_values("foo", :bar, :bax) }
22
22
  assert_correction ":bar", error.corrections
23
- assert_match "Did you mean? :bar", error.to_s
23
+ assert_match "Did you mean? :bar", get_message(error)
24
24
 
25
25
  error = assert_raise(KeyError) { hash.fetch_values("foo", :bar, "fooo") }
26
26
  assert_correction %("foo"), error.corrections
27
- assert_match %(Did you mean? "foo"), error.to_s
27
+ assert_match %(Did you mean? "foo"), get_message(error)
28
28
  end
29
29
 
30
30
  def test_correct_symbolized_hash_keys_with_string_value
@@ -32,13 +32,13 @@ class KeyNameCheckTest < Test::Unit::TestCase
32
32
 
33
33
  error = assert_raise(KeyError) { hash.fetch('foo_1') }
34
34
  assert_correction %(:foo_1), error.corrections
35
- assert_match %(Did you mean? :foo_1), error.to_s
35
+ assert_match %(Did you mean? :foo_1), get_message(error)
36
36
  end
37
37
 
38
38
  def test_corrects_sprintf_key_name
39
39
  error = assert_raise(KeyError) { sprintf("%<foo>d", {fooo: 1}) }
40
40
  assert_correction ":fooo", error.corrections
41
- assert_match "Did you mean? :fooo", error.to_s
41
+ assert_match "Did you mean? :fooo", get_message(error)
42
42
  end
43
43
 
44
44
  def test_corrects_env_key_name
@@ -46,7 +46,7 @@ class KeyNameCheckTest < Test::Unit::TestCase
46
46
  ENV["BAR"] = "2"
47
47
  error = assert_raise(KeyError) { ENV.fetch("BAX") }
48
48
  assert_correction %("BAR"), error.corrections
49
- assert_match %(Did you mean? "BAR"), error.to_s
49
+ assert_match %(Did you mean? "BAR"), get_message(error)
50
50
  ensure
51
51
  ENV.delete("FOO")
52
52
  ENV.delete("BAR")
@@ -41,28 +41,28 @@ class MethodNameCheckTest < Test::Unit::TestCase
41
41
  error = assert_raise(NoMethodError){ @user.flrst_name }
42
42
 
43
43
  assert_correction :first_name, error.corrections
44
- assert_match "Did you mean? first_name", error.to_s
44
+ assert_match "Did you mean? first_name", get_message(error)
45
45
  end
46
46
 
47
47
  def test_corrections_include_private_method
48
48
  error = assert_raise(NoMethodError){ @user.friend }
49
49
 
50
50
  assert_correction :friends, error.corrections
51
- assert_match "Did you mean? friends", error.to_s
51
+ assert_match "Did you mean? friends", get_message(error)
52
52
  end
53
53
 
54
54
  def test_corrections_include_method_from_module
55
55
  error = assert_raise(NoMethodError){ @user.fr0m_module }
56
56
 
57
57
  assert_correction :from_module, error.corrections
58
- assert_match "Did you mean? from_module", error.to_s
58
+ assert_match "Did you mean? from_module", get_message(error)
59
59
  end
60
60
 
61
61
  def test_corrections_include_class_method
62
62
  error = assert_raise(NoMethodError){ User.l0ad }
63
63
 
64
64
  assert_correction :load, error.corrections
65
- assert_match "Did you mean? load", error.to_s
65
+ assert_match "Did you mean? load", get_message(error)
66
66
  end
67
67
 
68
68
  def test_private_methods_should_not_be_suggested
@@ -77,7 +77,7 @@ class MethodNameCheckTest < Test::Unit::TestCase
77
77
  error = assert_raise(NoMethodError){ @user.call_incorrect_private_method }
78
78
 
79
79
  assert_correction :raise, error.corrections
80
- assert_match "Did you mean? raise", error.to_s
80
+ assert_match "Did you mean? raise", get_message(error)
81
81
  end
82
82
 
83
83
  def test_exclude_methods_on_nil
@@ -104,7 +104,7 @@ class MethodNameCheckTest < Test::Unit::TestCase
104
104
  end
105
105
  end
106
106
 
107
- assert_equal 1, error.to_s.scan(/Did you mean/).count
107
+ assert_equal 1, get_message(error).scan(/Did you mean/).count
108
108
  end
109
109
 
110
110
  def test_does_not_append_suggestions_three_times
@@ -116,7 +116,7 @@ class MethodNameCheckTest < Test::Unit::TestCase
116
116
  end
117
117
  end
118
118
 
119
- assert_equal 1, error.to_s.scan(/Did you mean/).count
119
+ assert_equal 1, get_message(error).scan(/Did you mean/).count
120
120
  end
121
121
 
122
122
  def test_suggests_corrections_on_nested_error
@@ -128,20 +128,20 @@ class MethodNameCheckTest < Test::Unit::TestCase
128
128
  end
129
129
  end
130
130
 
131
- assert_equal 1, error.to_s.scan(/Did you mean/).count
131
+ assert_equal 1, get_message(error).scan(/Did you mean/).count
132
132
  end
133
133
 
134
134
  def test_suggests_yield
135
135
  error = assert_raise(NoMethodError) { yeild(1) }
136
136
 
137
137
  assert_correction :yield, error.corrections
138
- assert_match "Did you mean? yield", error.to_s
138
+ assert_match "Did you mean? yield", get_message(error)
139
139
  end
140
140
 
141
141
  def test_does_not_suggest_yield
142
142
  error = assert_raise(NoMethodError) { 1.yeild }
143
143
 
144
144
  assert_correction [], error.corrections
145
- assert_not_match(/Did you mean\? +yield/, error.to_s)
145
+ assert_not_match(/Did you mean\? +yield/, get_message(error))
146
146
  end if RUBY_ENGINE != "jruby"
147
147
  end
@@ -0,0 +1,20 @@
1
+ require_relative '../helper'
2
+
3
+ return if !defined?(::NoMatchingPatternKeyError)
4
+
5
+ class PatternKeyNameCheckTest < Test::Unit::TestCase
6
+ include DidYouMean::TestHelper
7
+
8
+ def test_corrects_hash_key_name_with_single_pattern_match
9
+ error = assert_raise(NoMatchingPatternKeyError) do
10
+ eval(<<~RUBY, binding, __FILE__, __LINE__)
11
+ hash = {foo: 1, bar: 2, baz: 3}
12
+ hash => {fooo:}
13
+ fooo = 1 # suppress "unused variable: fooo" warning
14
+ RUBY
15
+ end
16
+
17
+ assert_correction ":foo", error.corrections
18
+ assert_match "Did you mean? :foo", get_message(error)
19
+ end
20
+ end
@@ -11,7 +11,7 @@ class RequirePathCheckTest < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  assert_correction 'ostruct', error.corrections
14
- assert_match "Did you mean? ostruct", error.to_s
14
+ assert_match "Did you mean? ostruct", get_message(error)
15
15
  end
16
16
 
17
17
  def test_load_error_from_require_for_nested_files_has_suggestions
@@ -20,13 +20,13 @@ class RequirePathCheckTest < Test::Unit::TestCase
20
20
  end
21
21
 
22
22
  assert_correction 'net/http', error.corrections
23
- assert_match "Did you mean? net/http", error.to_s
23
+ assert_match "Did you mean? net/http", get_message(error)
24
24
 
25
25
  error = assert_raise LoadError do
26
26
  require 'net-http'
27
27
  end
28
28
 
29
29
  assert_correction ['net/http', 'net/https'], error.corrections
30
- assert_match "Did you mean? net/http", error.to_s
30
+ assert_match "Did you mean? net/http", get_message(error)
31
31
  end
32
32
  end
@@ -10,6 +10,6 @@ class UncorrectableNameCheckTest < Test::Unit::TestCase
10
10
  end
11
11
 
12
12
  def test_message
13
- assert_equal "Other name error", @error.message
13
+ assert_not_match(/Did you mean\?/, @error.message)
14
14
  end
15
15
  end
@@ -39,7 +39,7 @@ class VariableNameCheckTest < Test::Unit::TestCase
39
39
  end
40
40
 
41
41
  assert_correction :first_name, error.corrections
42
- assert_match "Did you mean? first_name", error.to_s
42
+ assert_match "Did you mean? first_name", get_message(error)
43
43
  end
44
44
 
45
45
  def test_corrections_include_method_from_module
@@ -48,7 +48,7 @@ class VariableNameCheckTest < Test::Unit::TestCase
48
48
  end
49
49
 
50
50
  assert_correction :from_module, error.corrections
51
- assert_match "Did you mean? from_module", error.to_s
51
+ assert_match "Did you mean? from_module", get_message(error)
52
52
  end
53
53
 
54
54
  def test_corrections_include_local_variable_name
@@ -57,7 +57,7 @@ class VariableNameCheckTest < Test::Unit::TestCase
57
57
  error = (eprson rescue $!) # Do not use @assert_raise here as it changes a scope.
58
58
 
59
59
  assert_correction :person, error.corrections
60
- assert_match "Did you mean? person", error.to_s
60
+ assert_match "Did you mean? person", get_message(error)
61
61
  end
62
62
  end
63
63
 
@@ -81,30 +81,30 @@ class VariableNameCheckTest < Test::Unit::TestCase
81
81
  end
82
82
 
83
83
  assert_correction :false, false_error.corrections
84
- assert_match "Did you mean? false", false_error.to_s
84
+ assert_match "Did you mean? false", get_message(false_error)
85
85
 
86
86
  assert_correction :true, true_error.corrections
87
- assert_match "Did you mean? true", true_error.to_s
87
+ assert_match "Did you mean? true", get_message(true_error)
88
88
 
89
89
  assert_correction :nil, nil_error.corrections
90
- assert_match "Did you mean? nil", nil_error.to_s
90
+ assert_match "Did you mean? nil", get_message(nil_error)
91
91
 
92
92
  assert_correction :__FILE__, file_error.corrections
93
- assert_match "Did you mean? __FILE__", file_error.to_s
93
+ assert_match "Did you mean? __FILE__", get_message(file_error)
94
94
  end
95
95
 
96
96
  def test_suggests_yield
97
97
  error = assert_raise(NameError) { yeild }
98
98
 
99
99
  assert_correction :yield, error.corrections
100
- assert_match "Did you mean? yield", error.to_s
100
+ assert_match "Did you mean? yield", get_message(error)
101
101
  end
102
102
 
103
103
  def test_corrections_include_instance_variable_name
104
104
  error = assert_raise(NameError){ @user.to_s }
105
105
 
106
106
  assert_correction :@email_address, error.corrections
107
- assert_match "Did you mean? @email_address", error.to_s
107
+ assert_match "Did you mean? @email_address", get_message(error)
108
108
  end
109
109
 
110
110
  def test_corrections_include_private_method
@@ -113,7 +113,7 @@ class VariableNameCheckTest < Test::Unit::TestCase
113
113
  end
114
114
 
115
115
  assert_correction :cia_codename, error.corrections
116
- assert_match "Did you mean? cia_codename", error.to_s
116
+ assert_match "Did you mean? cia_codename", get_message(error)
117
117
  end
118
118
 
119
119
  @@does_exist = true
@@ -122,7 +122,7 @@ class VariableNameCheckTest < Test::Unit::TestCase
122
122
  error = assert_raise(NameError){ @@doesnt_exist }
123
123
 
124
124
  assert_correction :@@does_exist, error.corrections
125
- assert_match "Did you mean? @@does_exist", error.to_s
125
+ assert_match "Did you mean? @@does_exist", get_message(error)
126
126
  end
127
127
 
128
128
  def test_struct_name_error
@@ -130,11 +130,23 @@ class VariableNameCheckTest < Test::Unit::TestCase
130
130
  error = assert_raise(NameError){ value[:doesnt_exist] }
131
131
 
132
132
  assert_correction [:does_exist, :does_exist=], error.corrections
133
- assert_match "Did you mean? does_exist", error.to_s
133
+ assert_match "Did you mean? does_exist", get_message(error)
134
134
  end
135
135
 
136
136
  def test_exclude_typical_incorrect_suggestions
137
137
  error = assert_raise(NameError){ foo }
138
138
  assert_empty error.corrections
139
139
  end
140
+
141
+ def test_exclude_duplicates_with_same_name
142
+ error = assert_raise(NameError) do
143
+ eval(<<~RUBY, binding, __FILE__, __LINE__)
144
+ bar = 1
145
+ def bar;end
146
+ zar
147
+ RUBY
148
+ end
149
+
150
+ assert_correction [:bar], error.corrections
151
+ end
140
152
  end