diff --git a/lib/mutant/bootstrap.rb b/lib/mutant/bootstrap.rb index 555173e59..54e2a8a70 100644 --- a/lib/mutant/bootstrap.rb +++ b/lib/mutant/bootstrap.rb @@ -20,7 +20,7 @@ module Bootstrap '%s#name from: %s raised an error: %s' CLASS_NAME_TYPE_MISMATCH_FORMAT = - '%s#name from: %s returned %s' + '%s#name from: %s returned %s' private_constant(*constants(false)) @@ -139,9 +139,9 @@ def self.matchable_scopes(env) env.record(__method__) do config = env.config - scopes = env.world.object_space.each_object(Module).with_object([]) do |scope, aggregate| - expression = expression(config.reporter, config.expression_parser, scope) || next - aggregate << Scope.new(raw: scope, expression: expression) + scopes = env.world.object_space.each_object(Module).with_object([]) do |raw_scope, aggregate| + expression = expression(config.reporter, config.expression_parser, raw_scope) || next + aggregate << Scope.new(raw: raw_scope, expression: expression) end scopes.sort_by { |scope| scope.expression.syntax } @@ -149,31 +149,31 @@ def self.matchable_scopes(env) end private_class_method :matchable_scopes - def self.scope_name(reporter, scope) - scope.name + def self.scope_name(reporter, raw_scope) + raw_scope.name rescue => exception semantics_warning( reporter, CLASS_NAME_RAISED_EXCEPTION, exception: exception.inspect, - scope: scope, - scope_class: scope.class + scope: raw_scope, + scope_class: raw_scope.class ) nil end private_class_method :scope_name # rubocop:disable Metrics/MethodLength - def self.expression(reporter, expression_parser, scope) - name = scope_name(reporter, scope) or return + def self.expression(reporter, expression_parser, raw_scope) + name = scope_name(reporter, raw_scope) or return unless name.instance_of?(String) semantics_warning( reporter, CLASS_NAME_TYPE_MISMATCH_FORMAT, name: name, - scope_class: scope.class, - scope: scope + scope_class: raw_scope.class, + raw_scope: raw_scope ) return end diff --git a/lib/mutant/context.rb b/lib/mutant/context.rb index 0b7fc27da..1828d1f3c 100644 --- a/lib/mutant/context.rb +++ b/lib/mutant/context.rb @@ -12,8 +12,8 @@ class Context # # @return [Parser::AST::Node] def root(node) - nesting.reverse.reduce(node) do |current, scope| - self.class.wrap(scope, current) + nesting.reverse.reduce(node) do |current, raw_scope| + self.class.wrap(raw_scope, current) end end @@ -21,22 +21,13 @@ def root(node) # # @return [String] def identification - scope.name + scope.raw.name end # Wrap node into ast node - # - # @param [Class, Module] scope - # @param [Parser::AST::Node] node - # - # @return [Parser::AST::Class] - # if scope is of kind Class - # - # @return [Parser::AST::Module] - # if scope is of kind module - def self.wrap(scope, node) - name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym) - case scope + def self.wrap(raw_scope, node) + name = s(:const, nil, raw_scope.name.split(NAMESPACE_DELIMITER).last.to_sym) + case raw_scope when Class s(:class, name, nil, node) when Module @@ -77,7 +68,7 @@ def match_expressions private def name_nesting - scope.name.split(NAMESPACE_DELIMITER) + scope.raw.name.split(NAMESPACE_DELIMITER) end memoize :name_nesting diff --git a/lib/mutant/expression/method.rb b/lib/mutant/expression/method.rb index b1fd67bf9..ecfcb6171 100644 --- a/lib/mutant/expression/method.rb +++ b/lib/mutant/expression/method.rb @@ -78,7 +78,10 @@ def self.valid_method_name?(name) private def scope - Object.const_get(scope_name) + Scope.new( + raw: Object.const_get(scope_name), + expression: Namespace::Exact.new(scope_name: scope_name) + ) end end # Method diff --git a/lib/mutant/expression/methods.rb b/lib/mutant/expression/methods.rb index 6a5df5647..89bd81432 100644 --- a/lib/mutant/expression/methods.rb +++ b/lib/mutant/expression/methods.rb @@ -57,7 +57,10 @@ def match_length(expression) private def scope - Object.const_get(scope_name) + Scope.new( + expression: Namespace::Exact.new(scope_name: scope_name), + raw: Object.const_get(scope_name) + ) end end # Methods diff --git a/lib/mutant/expression/namespace.rb b/lib/mutant/expression/namespace.rb index e44af252c..1319f2053 100644 --- a/lib/mutant/expression/namespace.rb +++ b/lib/mutant/expression/namespace.rb @@ -66,10 +66,10 @@ class Exact < self # # @return [Matcher] def matcher - scope = find_scope + raw_scope = find_raw_scope - if scope - Matcher::Scope.new(scope: scope) + if raw_scope + Matcher::Scope.new(scope: Scope.new(expression: self, raw: raw_scope)) else Matcher::Null.new end @@ -83,7 +83,7 @@ def matcher private - def find_scope + def find_raw_scope Object.const_get(scope_name) rescue NameError # rubocop:disable Lint/SuppressedException end diff --git a/lib/mutant/matcher/descendants.rb b/lib/mutant/matcher/descendants.rb index 5f73dc635..574d95a43 100644 --- a/lib/mutant/matcher/descendants.rb +++ b/lib/mutant/matcher/descendants.rb @@ -10,7 +10,7 @@ def call(env) const = env.world.try_const_get(const_name) or return EMPTY_ARRAY Chain.new( - matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope.raw) } + matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope) } ).call(env) end diff --git a/lib/mutant/matcher/method.rb b/lib/mutant/matcher/method.rb index bbc54bf3b..4b9f07dfe 100644 --- a/lib/mutant/matcher/method.rb +++ b/lib/mutant/matcher/method.rb @@ -151,9 +151,9 @@ def visibility # end # # Change to this once 3.0 is EOL. - if scope.private_methods.include?(method_name) + if scope.raw.private_methods.include?(method_name) :private - elsif scope.protected_methods.include?(method_name) + elsif scope.raw.protected_methods.include?(method_name) :protected else :public diff --git a/lib/mutant/matcher/method/instance.rb b/lib/mutant/matcher/method/instance.rb index 19556a8d3..4e2015051 100644 --- a/lib/mutant/matcher/method/instance.rb +++ b/lib/mutant/matcher/method/instance.rb @@ -8,7 +8,7 @@ class Instance < self # Dispatching builder, detects memoizable case # - # @param [Class, Module] scope + # @param [Scope] scope # @param [UnboundMethod] method # # @return [Matcher::Method::Instance] @@ -31,7 +31,7 @@ def self.new(scope:, target_method:) # rubocop:enable Metrics/MethodLength def self.memoized_method?(scope, method_name) - scope < Adamantium && scope.memoized?(method_name) + scope.raw < Adamantium && scope.raw.memoized?(method_name) end private_class_method :memoized_method? @@ -48,9 +48,9 @@ def match?(node) end def visibility - if scope.private_instance_methods.include?(method_name) + if scope.raw.private_instance_methods.include?(method_name) :private - elsif scope.protected_instance_methods.include?(method_name) + elsif scope.raw.protected_instance_methods.include?(method_name) :protected else :public @@ -65,6 +65,7 @@ class Memoized < self def source_location scope + .raw .unmemoized_instance_method(method_name) .source_location end diff --git a/lib/mutant/matcher/methods.rb b/lib/mutant/matcher/methods.rb index c6aa50f8a..150bb725b 100644 --- a/lib/mutant/matcher/methods.rb +++ b/lib/mutant/matcher/methods.rb @@ -57,11 +57,11 @@ class Singleton < self private def access(_env, method_name) - scope.method(method_name) + scope.raw.method(method_name) end def candidate_scope - scope.singleton_class + scope.raw.singleton_class end end # Singleton @@ -73,11 +73,11 @@ class Metaclass < self private def access(_env, method_name) - scope.method(method_name) + scope.raw.method(method_name) end def candidate_scope - scope.singleton_class + scope.raw.singleton_class end end # Metaclass @@ -105,18 +105,19 @@ class Instance < self private # rubocop:disable Lint/RescueException + # mutant:disable - unstable source locations under < ruby-3.2 def access(env, method_name) - scope.instance_method(method_name) + candidate_scope.instance_method(method_name) rescue Exception => exception env.warn( - MESSAGE % { scope: scope, method_name: method_name, exception: exception } + MESSAGE % { scope: scope, method_name: method_name, exception: exception.inspect } ) nil end # rubocop:enable Lint/RescueException def candidate_scope - scope + scope.raw end end # Instance diff --git a/lib/mutant/matcher/namespace.rb b/lib/mutant/matcher/namespace.rb index 9b056bd6f..bfcb6a86f 100644 --- a/lib/mutant/matcher/namespace.rb +++ b/lib/mutant/matcher/namespace.rb @@ -13,7 +13,7 @@ class Namespace < self # @return [Enumerable] def call(env) Chain.new( - matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope.raw) } + matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope) } ).call(env) end diff --git a/lib/mutant/meta/example.rb b/lib/mutant/meta/example.rb index 06531c665..eaa547b36 100644 --- a/lib/mutant/meta/example.rb +++ b/lib/mutant/meta/example.rb @@ -39,7 +39,7 @@ def identification # @return [Context] def context Context.new( - scope: Object, + scope: scope, source_path: location.path ) end @@ -65,6 +65,15 @@ def generated end memoize :generated + private + + def scope + Scope.new( + expression: Expression::Namespace::Exact.new(scope_name: 'Object'), + raw: Object + ) + end + end # Example end # Meta end # Mutant diff --git a/lib/mutant/subject/method.rb b/lib/mutant/subject/method.rb index 3d150d29b..7659d9454 100644 --- a/lib/mutant/subject/method.rb +++ b/lib/mutant/subject/method.rb @@ -20,7 +20,7 @@ def expression Expression::Method.new( method_name: name.to_s, scope_symbol: self.class::SYMBOL, - scope_name: scope.name + scope_name: scope.raw.name ) end memoize :expression diff --git a/lib/mutant/subject/method/instance.rb b/lib/mutant/subject/method/instance.rb index 528287519..2d61d7e11 100644 --- a/lib/mutant/subject/method/instance.rb +++ b/lib/mutant/subject/method/instance.rb @@ -13,12 +13,12 @@ class Instance < self # # @return [self] def prepare - scope.undef_method(name) + scope.raw.undef_method(name) self end def post_insert - scope.__send__(visibility, name) + scope.raw.__send__(visibility, name) self end @@ -31,6 +31,7 @@ class Memoized < self # @return [self] def prepare scope + .raw .instance_variable_get(:@memoized_methods) .delete(name) diff --git a/lib/mutant/subject/method/metaclass.rb b/lib/mutant/subject/method/metaclass.rb index 867b29faa..075ace507 100644 --- a/lib/mutant/subject/method/metaclass.rb +++ b/lib/mutant/subject/method/metaclass.rb @@ -15,7 +15,7 @@ class Metaclass < self # # @return [self] def prepare - scope.singleton_class.public_send(:undef_method, name) + scope.raw.singleton_class.undef_method(name) self end diff --git a/lib/mutant/subject/method/singleton.rb b/lib/mutant/subject/method/singleton.rb index 353180ba5..de89fe61d 100644 --- a/lib/mutant/subject/method/singleton.rb +++ b/lib/mutant/subject/method/singleton.rb @@ -13,12 +13,12 @@ class Singleton < self # # @return [self] def prepare - scope.singleton_class.__send__(:undef_method, name) + scope.raw.singleton_class.undef_method(name) self end def post_insert - scope.singleton_class.__send__(visibility, name) + scope.raw.singleton_class.__send__(visibility, name) self end diff --git a/spec/support/shared_context.rb b/spec/support/shared_context.rb index ce0803c3b..324ddb22d 100644 --- a/spec/support/shared_context.rb +++ b/spec/support/shared_context.rb @@ -119,9 +119,16 @@ def setup_shared_context ) end + let(:scope) do + Mutant::Scope.new( + expression: Mutant::Expression::Namespace::Exact.new(scope_name: 'Object'), + raw: Object + ) + end + let(:subject_a_context) do Mutant::Context.new( - scope: Object, + scope: scope, source_path: 'suvject-a.rb' ) end diff --git a/spec/unit/mutant/bootstrap_spec.rb b/spec/unit/mutant/bootstrap_spec.rb index 9a3435891..14d202457 100644 --- a/spec/unit/mutant/bootstrap_spec.rb +++ b/spec/unit/mutant/bootstrap_spec.rb @@ -298,8 +298,15 @@ def object.name ) end + let(:scope) do + Mutant::Scope.new( + expression: parse_expression('TestApp::Literal'), + raw: TestApp::Literal + ) + end + let(:expected_subjects) do - Mutant::Matcher::Scope.new(scope: TestApp::Literal).call(env_initial) + Mutant::Matcher::Scope.new(scope: scope).call(env_initial) end let(:expected_env) do @@ -330,7 +337,7 @@ def object.name ) Mutant::Matcher::Scope - .new(scope: TestApp::Literal) + .new(scope: scope) .call(Mutant::Env.empty(world, config)).last end diff --git a/spec/unit/mutant/cli_spec.rb b/spec/unit/mutant/cli_spec.rb index 4c00ec97b..2f2a1845f 100644 --- a/spec/unit/mutant/cli_spec.rb +++ b/spec/unit/mutant/cli_spec.rb @@ -739,10 +739,17 @@ def self.main_body ) end + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Object + ) + end + let(:subject_a) do Mutant::Subject::Method::Instance.new( config: Mutant::Subject::Config::DEFAULT, - context: Mutant::Context.new(scope: Object, source_path: 'subject.rb'), + context: Mutant::Context.new(scope: scope, source_path: 'subject.rb'), node: s(:def, :send, s(:args), nil), visibility: :public ) diff --git a/spec/unit/mutant/context_spec.rb b/spec/unit/mutant/context_spec.rb index fd5e14580..d9a05552f 100644 --- a/spec/unit/mutant/context_spec.rb +++ b/spec/unit/mutant/context_spec.rb @@ -2,12 +2,12 @@ RSpec.describe Mutant::Context do describe '.wrap' do - subject { described_class.wrap(scope, node) } + subject { described_class.wrap(raw_scope, node) } let(:node) { s(:str, 'test') } context 'with Module as scope' do - let(:scope) { Mutant } + let(:raw_scope) { Mutant } let(:expected) do s(:module, @@ -19,7 +19,7 @@ end context 'with Class as scope' do - let(:scope) { Mutant::Context } + let(:raw_scope) { Mutant::Context } let(:expected) do s(:class, @@ -34,12 +34,18 @@ let(:object) { described_class.new(scope: scope, source_path: source_path) } let(:source_path) { instance_double(Pathname) } - let(:scope) { TestApp::Literal } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp::Literal + ) + end describe '#identification' do subject { object.identification } - it { should eql(scope.name) } + it { should eql(scope.raw.name) } end describe '#root' do @@ -70,7 +76,12 @@ class Literal subject { object.unqualified_name } context 'with top level constant name' do - let(:scope) { TestApp } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp + ) + end it 'should return the unqualified name' do should eql('TestApp') @@ -92,7 +103,12 @@ class Literal subject { object.match_expressions } context 'on toplevel scope' do - let(:scope) { TestApp } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp + ) + end it { should eql([parse_expression('TestApp*')]) } end diff --git a/spec/unit/mutant/expression/method_spec.rb b/spec/unit/mutant/expression/method_spec.rb index f65e50d8c..a35feb742 100644 --- a/spec/unit/mutant/expression/method_spec.rb +++ b/spec/unit/mutant/expression/method_spec.rb @@ -30,6 +30,17 @@ context 'with an instance method' do let(:input) { instance_method } + it 'uses expected scope' do + expect(subject.matcher.matchers.map(&:scope)).to eql( + [ + Mutant::Scope.new( + expression: Mutant::Expression::Namespace::Exact.new(scope_name: 'TestApp::Literal'), + raw: TestApp::Literal + ) + ] + ) + end + it 'returns correct matcher' do expect(subject.call(env).map(&:expression)).to eql([object]) end diff --git a/spec/unit/mutant/expression/methods_spec.rb b/spec/unit/mutant/expression/methods_spec.rb index c7dd297ae..ccaf76e63 100644 --- a/spec/unit/mutant/expression/methods_spec.rb +++ b/spec/unit/mutant/expression/methods_spec.rb @@ -50,13 +50,20 @@ describe '#matcher' do subject { object.matcher } + let(:scope) do + Mutant::Scope.new( + expression: Mutant::Expression::Namespace::Exact.new(scope_name: 'TestApp::Literal'), + raw: TestApp::Literal + ) + end + context 'with an instance method' do let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } } specify do should eql( Mutant::Matcher::Chain.new( - matchers: [Mutant::Matcher::Methods::Instance.new(scope: TestApp::Literal)] + matchers: [Mutant::Matcher::Methods::Instance.new(scope: scope)] ) ) end @@ -69,8 +76,8 @@ should eql( Mutant::Matcher::Chain.new( matchers: [ - Mutant::Matcher::Methods::Singleton.new(scope: TestApp::Literal), - Mutant::Matcher::Methods::Metaclass.new(scope: TestApp::Literal) + Mutant::Matcher::Methods::Singleton.new(scope: scope), + Mutant::Matcher::Methods::Metaclass.new(scope: scope) ] ) ) diff --git a/spec/unit/mutant/expression/namespace/exact_spec.rb b/spec/unit/mutant/expression/namespace/exact_spec.rb index ec4c61006..47644a3c4 100644 --- a/spec/unit/mutant/expression/namespace/exact_spec.rb +++ b/spec/unit/mutant/expression/namespace/exact_spec.rb @@ -7,6 +7,13 @@ describe '#matcher' do subject { object.matcher } + let(:scope) do + Mutant::Scope.new( + expression: Mutant::Expression::Namespace::Exact.new(scope_name: 'TestApp::Literal'), + raw: TestApp::Literal + ) + end + context 'when constant does not exist' do let(:input) { 'TestApp::DoesNotExist' } @@ -14,7 +21,7 @@ end context 'when constant exists' do - it { should eql(Mutant::Matcher::Scope.new(scope: TestApp::Literal)) } + it { should eql(Mutant::Matcher::Scope.new(scope: scope)) } end end diff --git a/spec/unit/mutant/matcher/descendants_spec.rb b/spec/unit/mutant/matcher/descendants_spec.rb index f7b0c46fa..ba1c6d8eb 100644 --- a/spec/unit/mutant/matcher/descendants_spec.rb +++ b/spec/unit/mutant/matcher/descendants_spec.rb @@ -15,7 +15,10 @@ def apply Mutant::Subject::Method::Instance.new( config: Mutant::Subject::Config::DEFAULT, context: Mutant::Context.new( - scope: TestApp::Foo::Bar::Baz, + scope: Mutant::Scope.new( + raw: TestApp::Foo::Bar::Baz, + expression: parse_expression('TestApp::Foo::Bar::Baz') + ), source_path: TestApp::ROOT.join('lib/test_app.rb') ), node: s(:def, :foo, s(:args), nil), diff --git a/spec/unit/mutant/matcher/method/instance_spec.rb b/spec/unit/mutant/matcher/method/instance_spec.rb index ff42410a2..dc985d073 100644 --- a/spec/unit/mutant/matcher/method/instance_spec.rb +++ b/spec/unit/mutant/matcher/method/instance_spec.rb @@ -4,7 +4,7 @@ subject { object.call(env) } let(:base) { TestApp::InstanceMethodTests } - let(:method) { scope.instance_method(method_name) } + let(:method) { scope.raw.instance_method(method_name) } let(:method_arity) { 0 } let(:method_name) { :foo } let(:object) { described_class.new(scope: scope, target_method: method) } @@ -40,7 +40,13 @@ def arguments end context 'when method is defined inside file that does not end with .rb' do - let(:scope) { base::WithMemoizer } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithMemoizer + ) + end + let(:source_location) { [file, instance_double(Integer)] } let(:file) { 'example.erb' } @@ -72,7 +78,13 @@ def arguments let(:expected_warnings) { [] } let(:method_line) { 13 } let(:method_name) { :bar } - let(:scope) { base::WithMemoizer } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithMemoizer + ) + end before do allow(diff_a).to receive(:touches_path?).with(source_path).and_return(diff_a_touches?) @@ -120,8 +132,14 @@ def arguments end context 'when method is defined inside eval' do - let(:scope) { base::WithMemoizer } - let(:method) { scope.instance_method(:boz) } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithMemoizer + ) + end + + let(:method) { scope.raw.instance_method(:boz) } let(:expected_warnings) do [ @@ -133,8 +151,14 @@ def arguments end context 'when method is defined without source location' do - let(:scope) { Module } - let(:method) { scope.instance_method(:object_id) } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Module + ) + end + + let(:method) { scope.raw.instance_method(:object_id) } let(:expected_warnings) do [ @@ -146,7 +170,12 @@ def arguments end context 'in module eval' do - let(:scope) { base::InModuleEval } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::InModuleEval + ) + end let(:expected_warnings) do [ @@ -158,7 +187,12 @@ def arguments end context 'in class eval' do - let(:scope) { base::InClassEval } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::InClassEval + ) + end let(:expected_warnings) do [ @@ -170,15 +204,22 @@ def arguments end context 'when method is defined once' do - let(:method_name) { :bar } - let(:scope) { base::WithMemoizer } - let(:method_line) { 13 } + let(:method_name) { :bar } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithMemoizer + ) + end + + let(:method_line) { 13 } it_should_behave_like 'a method matcher' let(:context) do Mutant::Context.new( - scope: TestApp::InstanceMethodTests::WithMemoizer, + scope: scope, source_path: MutantSpec::ROOT.join('test_app', 'lib', 'test_app.rb') ) end @@ -198,7 +239,7 @@ def arguments context 'with %s visibility' % visibility do let(:expected_visibility) { visibility } - before { context.scope.__send__(visibility, method_name) } + before { context.scope.raw.__send__(visibility, method_name) } it 'returns expected subjects' do expect(subject).to eql(expected_subjects) @@ -208,15 +249,27 @@ def arguments end context 'when method is defined once with a memoizer' do - let(:scope) { base::WithMemoizer } - let(:method_line) { 15 } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithMemoizer + ) + end + + let(:method_line) { 15 } it_should_behave_like 'a method matcher' end context 'when method is defined multiple times' do context 'on different lines' do - let(:scope) { base::DefinedMultipleTimes::DifferentLines } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::DifferentLines + ) + end + let(:method_line) { 24 } let(:method_arity) { 1 } @@ -224,7 +277,13 @@ def arguments end context 'on the same line' do - let(:scope) { base::DefinedMultipleTimes::SameLineSameScope } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLineSameScope + ) + end + let(:method_line) { 29 } let(:method_arity) { 1 } @@ -232,7 +291,13 @@ def arguments end context 'on the same line with different scope' do - let(:scope) { base::DefinedMultipleTimes::SameLineDifferentScope } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLineDifferentScope + ) + end + let(:method_line) { 33 } let(:method_arity) { 1 } @@ -241,7 +306,13 @@ def arguments end context 'with sorbet signature' do - let(:scope) { base::WithSignature } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithSignature + ) + end + let(:method_line) { 116 } let(:method_arity) { 0 } @@ -249,7 +320,13 @@ def arguments end context 'on delegate class' do - let(:scope) { TestApp::DelegateTest } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp::DelegateTest + ) + end + let(:method_line) { 134 } let(:method_arity) { 0 } @@ -257,7 +334,13 @@ def arguments end context 'on inline disabled method' do - let(:scope) { TestApp::InlineDisabled } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp::InlineDisabled + ) + end + let(:method_line) { 148 } let(:method_arity) { 0 } diff --git a/spec/unit/mutant/matcher/method/metaclass_spec.rb b/spec/unit/mutant/matcher/method/metaclass_spec.rb index 13bd1605f..e5dd2f384 100644 --- a/spec/unit/mutant/matcher/method/metaclass_spec.rb +++ b/spec/unit/mutant/matcher/method/metaclass_spec.rb @@ -6,7 +6,7 @@ subject { object.call(env) } let(:object) { described_class.new(scope: scope, target_method: method) } - let(:method) { scope.public_method(method_name) } + let(:method) { scope.raw.public_method(method_name) } let(:type) { :def } let(:method_name) { :foo } let(:method_arity) { 0 } @@ -38,7 +38,13 @@ def arguments end context 'when also defined on lvar' do - let(:scope) { base::DefinedOnLvar } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnLvar + ) + end + let(:expected_warnings) do [ 'Can only match :def inside :sclass on :self or :const, got :sclass on :lvar unable to match' @@ -49,30 +55,54 @@ def arguments end context 'when defined on self' do - let(:scope) { base::DefinedOnSelf } - let(:method_line) { 7 } + let(:method_line) { 7 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnSelf + ) + end it_should_behave_like 'a method matcher' context 'when scope is a metaclass' do - let(:scope) { base::DefinedOnSelf::InsideMetaclass.metaclass } let(:method_line) { 28 } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnSelf::InsideMetaclass.metaclass + ) + end + it_should_behave_like 'a method matcher' end end context 'when defined on constant' do context 'inside namespace' do - let(:scope) { base::DefinedOnConstant::InsideNamespace } - let(:method_line) { 44 } + let(:method_line) { 44 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnConstant::InsideNamespace + ) + end it_should_behave_like 'a method matcher' end context 'outside namespace' do - let(:scope) { base::DefinedOnConstant::OutsideNamespace } - let(:method_line) { 52 } + let(:method_line) { 52 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnConstant::OutsideNamespace + ) + end it_should_behave_like 'a method matcher' end @@ -80,16 +110,28 @@ def arguments context 'when defined multiple times in the same line' do context 'with method on different scope' do - let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope } - let(:method_line) { 76 } - let(:method_arity) { 1 } + let(:method_line) { 76 } + let(:method_arity) { 1 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLine::DifferentScope + ) + end it_should_behave_like 'a method matcher' end context 'with different name' do - let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentName } - let(:method_line) { 80 } + let(:method_line) { 80 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLine::DifferentName + ) + end it_should_behave_like 'a method matcher' end @@ -98,8 +140,14 @@ def arguments # tests that the evaluator correctly returns nil when the metaclass doesn't # directly contain the method context 'when defined inside a class in a metaclass' do - let(:scope) { base::NotActuallyInAMetaclass } - let(:method) { scope.metaclass::SomeClass.new.public_method(:foo) } + let(:method) { scope.raw.metaclass::SomeClass.new.public_method(:foo) } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::NotActuallyInAMetaclass + ) + end it { is_expected.to be_empty } end diff --git a/spec/unit/mutant/matcher/method/singleton_spec.rb b/spec/unit/mutant/matcher/method/singleton_spec.rb index e31be05bb..b90aa16af 100644 --- a/spec/unit/mutant/matcher/method/singleton_spec.rb +++ b/spec/unit/mutant/matcher/method/singleton_spec.rb @@ -4,7 +4,7 @@ subject { object.call(env) } let(:object) { described_class.new(scope: scope, target_method: method) } - let(:method) { scope.method(method_name) } + let(:method) { scope.raw.method(method_name) } let(:type) { :defs } let(:method_name) { :foo } let(:method_arity) { 0 } @@ -36,7 +36,13 @@ def arguments end context 'when also defined on lvar' do - let(:scope) { base::DefinedOnLvar } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnLvar + ) + end + let(:expected_warnings) do [ 'Can only match :defs on :self or :const got :lvar unable to match' @@ -47,14 +53,20 @@ def arguments end context 'when defined on self' do - let(:scope) { base::DefinedOnSelf } - let(:method_line) { 61 } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnSelf + ) + end + + let(:method_line) { 61 } it_should_behave_like 'a method matcher' do %i[public protected private].each do |visibility| context 'with %s visibility' % visibility do before do - scope.singleton_class.__send__(visibility, method_name) + scope.raw.singleton_class.__send__(visibility, method_name) end it 'returns expected subjects' do @@ -67,15 +79,27 @@ def arguments context 'when defined on constant' do context 'inside namespace' do - let(:scope) { base::DefinedOnConstant::InsideNamespace } - let(:method_line) { 71 } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnConstant::InsideNamespace + ) + end + + let(:method_line) { 71 } it_should_behave_like 'a method matcher' end context 'outside namespace' do - let(:scope) { base::DefinedOnConstant::OutsideNamespace } - let(:method_line) { 78 } + let(:method_line) { 78 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedOnConstant::OutsideNamespace + ) + end it_should_behave_like 'a method matcher' end @@ -83,33 +107,57 @@ def arguments context 'when defined multiple times in the same line' do context 'with method on different scope' do - let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope } - let(:method_line) { 97 } - let(:method_arity) { 1 } + let(:method_line) { 97 } + let(:method_arity) { 1 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLine::DifferentScope + ) + end it_should_behave_like 'a method matcher' end context 'with different name' do - let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentName } - let(:method_line) { 101 } + let(:method_line) { 101 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::DefinedMultipleTimes::SameLine::DifferentName + ) + end it_should_behave_like 'a method matcher' end end context 'with sorbet signature' do - let(:scope) { base::WithSignature } - let(:method_line) { 126 } - let(:method_arity) { 0 } + let(:method_line) { 126 } + let(:method_arity) { 0 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: base::WithSignature + ) + end it_should_behave_like 'a method matcher' end context 'on inline disabled method' do - let(:scope) { TestApp::InlineDisabled } - let(:method_line) { 152 } - let(:method_arity) { 0 } + let(:method_line) { 152 } + let(:method_arity) { 0 } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp::InlineDisabled + ) + end it_should_behave_like 'a method matcher' do it 'returns disabled inline config' do diff --git a/spec/unit/mutant/matcher/methods/instance_spec.rb b/spec/unit/mutant/matcher/methods/instance_spec.rb index 5d90a3cb8..8944e4a95 100644 --- a/spec/unit/mutant/matcher/methods/instance_spec.rb +++ b/spec/unit/mutant/matcher/methods/instance_spec.rb @@ -1,7 +1,14 @@ # frozen_string_literal: true RSpec.describe Mutant::Matcher::Methods::Instance, '#call' do - let(:object) { described_class.new(scope: class_under_test) } + let(:object) { described_class.new(scope: scope) } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: class_under_test + ) + end let(:env) do config = Fixtures::TEST_ENV.config @@ -76,7 +83,7 @@ def method_c; end expect(matcher).to receive(:call).with(env).and_return([subject]) expect(Mutant::Matcher::Method::Instance).to receive(:new) - .with(scope: class_under_test, target_method: class_under_test.instance_method(method)) + .with(scope: scope, target_method: class_under_test.instance_method(method)) .and_return(matcher) end end @@ -90,12 +97,15 @@ def method_c; end let(:object) { described_class.new(scope: scope) } let(:scope) do - Class.new do - def self.public_instance_methods(ancestors) - fail if ancestors - %i[foo] + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Class.new do + def self.public_instance_methods(ancestors) + fail if ancestors + %i[foo] + end end - end + ) end def apply @@ -111,14 +121,16 @@ def apply method_name = :foo + candidate_scope = scope.raw + exception = begin - scope.instance_method(method_name) + candidate_scope.instance_method(method_name) rescue NameError => exception exception end - expect(capture_reporter.warnings).to eql([<<~'MESSAGE' % { scope: scope, exception: exception }]) + expect(capture_reporter.warnings).to eql([<<~'MESSAGE' % { scope: scope, exception: exception.inspect }]) Caught an exception while accessing a method with #instance_method that is part of #{public,private,protected}_instance_methods. diff --git a/spec/unit/mutant/matcher/methods/metaclass_spec.rb b/spec/unit/mutant/matcher/methods/metaclass_spec.rb index 7de869bca..ef4430469 100644 --- a/spec/unit/mutant/matcher/methods/metaclass_spec.rb +++ b/spec/unit/mutant/matcher/methods/metaclass_spec.rb @@ -1,8 +1,15 @@ # frozen_string_literal: true RSpec.describe Mutant::Matcher::Methods::Metaclass, '#call' do - let(:object) { described_class.new(scope: class_under_test) } - let(:env) { Fixtures::TEST_ENV } + let(:object) { described_class.new(scope: scope) } + let(:env) { Fixtures::TEST_ENV } + + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: class_under_test + ) + end let(:class_under_test) do parent = Module.new do @@ -51,7 +58,7 @@ def method_c; end method_c: subject_c }.each do |method, subject| allow(matcher).to receive(:new) - .with(scope: class_under_test, target_method: class_under_test.method(method)) + .with(scope: scope, target_method: class_under_test.method(method)) .and_return(Mutant::Matcher::Static.new(subjects: [subject])) end end diff --git a/spec/unit/mutant/matcher/methods/singleton_spec.rb b/spec/unit/mutant/matcher/methods/singleton_spec.rb index 8eb2bbf5a..0c37f26be 100644 --- a/spec/unit/mutant/matcher/methods/singleton_spec.rb +++ b/spec/unit/mutant/matcher/methods/singleton_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true RSpec.describe Mutant::Matcher::Methods::Singleton, '#call' do - let(:object) { described_class.new(scope: class_under_test) } - let(:env) { Fixtures::TEST_ENV } + let(:object) { described_class.new(scope: scope) } + let(:env) { Fixtures::TEST_ENV } let(:class_under_test) do parent = Module.new do @@ -31,6 +31,13 @@ def self.method_c; end let(:subjects) { [subject_a, subject_b, subject_c] } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: class_under_test + ) + end + before do matcher = Mutant::Matcher::Method::Singleton @@ -40,7 +47,7 @@ def self.method_c; end method_c: subject_c }.each do |method, subject| allow(matcher).to receive(:new) - .with(scope: class_under_test, target_method: class_under_test.method(method)) + .with(scope: scope, target_method: class_under_test.method(method)) .and_return(Mutant::Matcher::Static.new(subjects: [subject])) end end diff --git a/spec/unit/mutant/matcher/namespace_spec.rb b/spec/unit/mutant/matcher/namespace_spec.rb index bec0bc83b..b27698167 100644 --- a/spec/unit/mutant/matcher/namespace_spec.rb +++ b/spec/unit/mutant/matcher/namespace_spec.rb @@ -22,7 +22,7 @@ expect(matcher).to receive(:call).with(env).and_return(subjects) expect(klass).to receive(:new) - .with(scope: scope) + .with(scope: Mutant::Scope.new(raw: scope, expression: parse_expression(scope.name))) .and_return(matcher) end diff --git a/spec/unit/mutant/matcher/scope_spec.rb b/spec/unit/mutant/matcher/scope_spec.rb index 7e5e44224..84fbb8e6f 100644 --- a/spec/unit/mutant/matcher/scope_spec.rb +++ b/spec/unit/mutant/matcher/scope_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true RSpec.describe Mutant::Matcher::Scope, '#call' do - let(:scope) { TestApp } let(:object) { described_class.new(scope: scope) } let(:env) { instance_double(Mutant::Env) } let(:matcher_a) { instance_double(Mutant::Matcher) } @@ -11,6 +10,13 @@ let(:subject_b) { instance_double(Mutant::Subject) } let(:subject_c) { instance_double(Mutant::Subject) } + let(:scope) do + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: TestApp + ) + end + subject { object.call(env) } before do diff --git a/spec/unit/mutant/meta/example_spec.rb b/spec/unit/mutant/meta/example_spec.rb index a4d87c0be..a1eca5514 100644 --- a/spec/unit/mutant/meta/example_spec.rb +++ b/spec/unit/mutant/meta/example_spec.rb @@ -53,7 +53,14 @@ describe '#context' do subject { object.context } - it { should eql(Mutant::Context.new(scope: Object, source_path: location.path)) } + let(:scope) do + Mutant::Scope.new( + expression: Mutant::Expression::Namespace::Exact.new(scope_name: 'Object'), + raw: Object + ) + end + + it { should eql(Mutant::Context.new(scope: scope, source_path: location.path)) } end describe '#identification' do diff --git a/spec/unit/mutant/subject/method/instance_spec.rb b/spec/unit/mutant/subject/method/instance_spec.rb index d858534b3..025e901d1 100644 --- a/spec/unit/mutant/subject/method/instance_spec.rb +++ b/spec/unit/mutant/subject/method/instance_spec.rb @@ -20,19 +20,22 @@ end let(:scope) do - Class.new do - attr_reader :bar + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Class.new do + attr_reader :bar - def initialize - @bar = :boo - end + def initialize + @bar = :boo + end - def foo; end + def foo; end - def self.name - 'Test' + def self.name + 'Test' + end end - end + ) end describe '#expression' do @@ -56,7 +59,7 @@ def self.name it 'undefines method on scope' do expect { subject } - .to change { scope.instance_methods.include?(:foo) } + .to change { scope.raw.instance_methods.include?(:foo) } .from(true) .to(false) end @@ -69,7 +72,7 @@ def self.name it 'sets method visibility' do expect { subject } - .to change { scope.private_instance_methods.include?(:foo) } + .to change { scope.raw.private_instance_methods.include?(:foo) } .from(false) .to(true) end @@ -99,16 +102,19 @@ def self.name shared_context 'memoizable scope setup' do let(:scope) do - Class.new do - include Unparser::Adamantium + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Class.new do + include Unparser::Adamantium - def self.name - 'MemoizableClass' - end + def self.name + 'MemoizableClass' + end - def foo; end - memoize :foo - end + def foo; end + memoize :foo + end + ) end end @@ -118,11 +124,11 @@ def foo; end subject { object.prepare } it 'undefines memoizer' do - expect { subject }.to change { scope.memoized?(:foo) }.from(true).to(false) + expect { subject }.to change { scope.raw.memoized?(:foo) }.from(true).to(false) end it 'undefines method on scope' do - expect { subject }.to change { scope.instance_methods.include?(:foo) }.from(true).to(false) + expect { subject }.to change { scope.raw.instance_methods.include?(:foo) }.from(true).to(false) end it_should_behave_like 'a command method' diff --git a/spec/unit/mutant/subject/method/metaclass_spec.rb b/spec/unit/mutant/subject/method/metaclass_spec.rb index c3b76eec9..4d47a2c9e 100644 --- a/spec/unit/mutant/subject/method/metaclass_spec.rb +++ b/spec/unit/mutant/subject/method/metaclass_spec.rb @@ -17,15 +17,18 @@ end let(:scope) do - Class.new do - class << self - def foo; end - - def name - 'Test' - end - end - end + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Class.new do + class << self + def foo; end + + def name + 'Test' + end + end + end + ) end describe '#expression' do @@ -49,7 +52,7 @@ def name subject { object.prepare } it 'undefines method on scope' do - expect { subject }.to change { scope.public_methods.include?(:foo) }.from(true).to(false) + expect { subject }.to change { scope.raw.public_methods.include?(:foo) }.from(true).to(false) end it_should_behave_like 'a command method' diff --git a/spec/unit/mutant/subject/method/singleton_spec.rb b/spec/unit/mutant/subject/method/singleton_spec.rb index 3424f1be8..8cad954ec 100644 --- a/spec/unit/mutant/subject/method/singleton_spec.rb +++ b/spec/unit/mutant/subject/method/singleton_spec.rb @@ -17,13 +17,16 @@ end let(:scope) do - Class.new do - def self.foo; end - - def self.name - 'Test' + Mutant::Scope.new( + expression: instance_double(Mutant::Expression), + raw: Class.new do + def self.foo; end + + def self.name + 'Test' + end end - end + ) end describe '#expression' do @@ -47,7 +50,7 @@ def self.name subject { object.prepare } it 'undefines method on scope' do - expect { subject }.to change { scope.public_methods.include?(:foo) }.from(true).to(false) + expect { subject }.to change { scope.raw.public_methods.include?(:foo) }.from(true).to(false) end it_should_behave_like 'a command method' @@ -58,7 +61,7 @@ def self.name it 'sets method visibility' do expect { subject } - .to change { scope.private_methods.include?(:foo) } + .to change { scope.raw.private_methods.include?(:foo) } .from(false) .to(true) end