From d04af61cb96862818ca504135d188e48c106e9fa Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Tue, 15 Dec 2020 23:34:05 +0300 Subject: [PATCH] Prevent should from circumventing target check By directly instantiating expectation handler, should/should_not was circumventing enforce_value_expectation check. subject(:action) { -> { raise } } it { should raise_error StandardError } would not print "The implicit block expectation syntax is deprecated", while subject(:action) { -> { raise } } it { is_expected.to raise_error StandardError } will. See https://github.com/rspec/rspec-expectations/pull/1139 --- lib/rspec/core/memoized_helpers.rb | 22 ++++++++++++++++++++++ spec/rspec/core/memoized_helpers_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index 771c12d716..87ee8e9359 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -78,6 +78,7 @@ def subject # @note If you are using RSpec's newer expect-based syntax you may # want to use `is_expected.to` instead of `should`. def should(matcher=nil, message=nil) + enforce_value_expectation(matcher, 'should') RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message) end @@ -97,6 +98,7 @@ def should(matcher=nil, message=nil) # @note If you are using RSpec's newer expect-based syntax you may # want to use `is_expected.to_not` instead of `should_not`. def should_not(matcher=nil, message=nil) + enforce_value_expectation(matcher, 'should_not') RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message) end @@ -144,6 +146,26 @@ def __init_memoized end end + # @private + def enforce_value_expectation(matcher, method_name) + return if matcher_supports_value_expectations?(matcher) + + RSpec.deprecate( + "#{method_name} #{RSpec::Support::ObjectFormatter.format(matcher)}", + :message => + "The implicit block expectation syntax is deprecated, you should pass " \ + "a block to `expect` to use the provided block expectation matcher " \ + "(#{RSpec::Support::ObjectFormatter.format(matcher)}), " \ + "or the matcher must implement `supports_value_expectations?`." + ) + end + + def matcher_supports_value_expectations?(matcher) + matcher.supports_value_expectations? + rescue + true + end + # @private class ThreadsafeMemoized def initialize diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index dc2da63f82..4987549853 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -630,6 +630,28 @@ def hello_message; "Hello from module"; end end end + RSpec.describe 'implicit block expectation syntax' do + matcher :block_matcher do + match { |actual| true } + supports_block_expectations + def supports_value_expectations? + false + end + end + + subject { 'value or a Proc' } + + it '`should` prints a deprecation warning when given a value' do + expect_warn_deprecation(/The implicit block expectation syntax is deprecated, you should pass/) + expect { should block_matcher }.not_to raise_error + end + + it '`should_not` prints a deprecation warning when given a value' do + expect_warn_deprecation(/The implicit block expectation syntax is deprecated, you should pass/) + expect { should_not block_matcher }.to raise_error(Exception) + end + end + RSpec.describe 'Module#define_method' do it 'retains its normal private visibility on Ruby versions where it is normally private', :if => RUBY_VERSION < '2.5' do a_module = Module.new