mutant 0.5.17 → 0.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +6 -0
  3. data/Rakefile +13 -0
  4. data/config/flay.yml +1 -1
  5. data/config/reek.yml +8 -1
  6. data/lib/mutant.rb +18 -3
  7. data/lib/mutant/cli.rb +6 -3
  8. data/lib/mutant/constants.rb +3 -0
  9. data/lib/mutant/expression.rb +110 -0
  10. data/lib/mutant/expression/method.rb +92 -0
  11. data/lib/mutant/expression/namespace.rb +90 -0
  12. data/lib/mutant/matcher/namespace.rb +3 -4
  13. data/lib/mutant/meta.rb +31 -0
  14. data/lib/mutant/meta/example.rb +152 -0
  15. data/lib/mutant/meta/example/dsl.rb +105 -0
  16. data/lib/mutant/mutator/node.rb +6 -6
  17. data/lib/mutant/mutator/node/arguments.rb +1 -1
  18. data/lib/mutant/mutator/node/begin.rb +1 -1
  19. data/lib/mutant/mutator/node/const.rb +1 -1
  20. data/lib/mutant/mutator/node/if.rb +6 -6
  21. data/lib/mutant/mutator/node/literal/array.rb +2 -2
  22. data/lib/mutant/mutator/node/literal/hash.rb +2 -2
  23. data/lib/mutant/mutator/node/literal/range.rb +3 -3
  24. data/lib/mutant/mutator/node/literal/regex.rb +2 -2
  25. data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
  26. data/lib/mutant/mutator/node/op_asgn.rb +1 -3
  27. data/lib/mutant/mutator/node/or_asgn.rb +31 -0
  28. data/lib/mutant/mutator/node/send.rb +1 -1
  29. data/lib/mutant/mutator/node/send/attribute_assignment.rb +1 -1
  30. data/lib/mutant/reporter/cli/report/mutation.rb +11 -1
  31. data/lib/mutant/strategy.rb +1 -1
  32. data/lib/mutant/version.rb +1 -1
  33. data/meta/and.rb +12 -0
  34. data/meta/and_asgn.rb +12 -0
  35. data/meta/array.rb +30 -0
  36. data/meta/begin.rb +15 -0
  37. data/meta/binary.rb +12 -0
  38. data/meta/block.rb +55 -0
  39. data/meta/block_pass.rb +8 -0
  40. data/meta/blockarg.rb +10 -0
  41. data/meta/boolean.rb +15 -0
  42. data/meta/break.rb +11 -0
  43. data/meta/case.rb +303 -0
  44. data/meta/casgn.rb +9 -0
  45. data/meta/cbase.rb +8 -0
  46. data/meta/const.rb +9 -0
  47. data/meta/cvar.rb +7 -0
  48. data/meta/cvasgn.rb +10 -0
  49. data/meta/define.rb +118 -0
  50. data/meta/defined.rb +7 -0
  51. data/meta/dstr.rb +10 -0
  52. data/meta/dsym.rb +11 -0
  53. data/meta/ensure.rb +9 -0
  54. data/meta/fixnum.rb +19 -0
  55. data/meta/float.rb +38 -0
  56. data/meta/gvar.rb +7 -0
  57. data/meta/gvasgn.rb +10 -0
  58. data/meta/hash.rb +25 -0
  59. data/meta/if.rb +57 -0
  60. data/meta/ivasgn.rb +10 -0
  61. data/meta/kwbegin.rb +9 -0
  62. data/meta/lvar.rb +14 -0
  63. data/meta/lvasgn.rb +10 -0
  64. data/meta/masgn.rb +7 -0
  65. data/meta/match_current_line.rb +14 -0
  66. data/meta/next.rb +11 -0
  67. data/meta/nil.rb +5 -0
  68. data/meta/nthref.rb +14 -0
  69. data/meta/op_assgn.rb +15 -0
  70. data/meta/or_asgn.rb +22 -0
  71. data/meta/range.rb +41 -0
  72. data/meta/redo.rb +5 -0
  73. data/meta/regex.rb +20 -0
  74. data/meta/rescue.rb +38 -0
  75. data/meta/restarg.rb +11 -0
  76. data/meta/return.rb +15 -0
  77. data/meta/self.rb +7 -0
  78. data/meta/send.rb +240 -0
  79. data/meta/string.rb +7 -0
  80. data/meta/super.rb +26 -0
  81. data/meta/symbol.rb +8 -0
  82. data/meta/unless.rb +15 -0
  83. data/meta/while.rb +24 -0
  84. data/meta/yield.rb +10 -0
  85. data/mutant.gemspec +1 -0
  86. data/spec/integration/mutant/corpus_spec.rb +29 -25
  87. data/spec/spec_helper.rb +2 -0
  88. data/spec/support/mutation_verifier.rb +1 -0
  89. data/spec/unit/mutant/cli_new_spec.rb +6 -6
  90. data/spec/unit/mutant/expression/method_spec.rb +50 -0
  91. data/spec/unit/mutant/expression/namespace/flat_spec.rb +32 -0
  92. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +37 -0
  93. data/spec/unit/mutant/matcher/namespace_spec.rb +2 -2
  94. data/spec/unit/mutant/mutation_spec.rb +1 -1
  95. data/spec/unit/mutant/mutator/node_spec.rb +14 -0
  96. metadata +123 -139
  97. data/lib/mutant/cli/classifier.rb +0 -139
  98. data/lib/mutant/cli/classifier/method.rb +0 -105
  99. data/lib/mutant/cli/classifier/namespace.rb +0 -49
  100. data/spec/unit/mutant/cli/classifier/method_spec.rb +0 -77
  101. data/spec/unit/mutant/cli/classifier/namespace/flat_spec.rb +0 -58
  102. data/spec/unit/mutant/cli/classifier/namespace/recursive_spec.rb +0 -58
  103. data/spec/unit/mutant/cli/classifier_spec.rb +0 -59
  104. data/spec/unit/mutant/mutator/node/and_asgn_spec.rb +0 -19
  105. data/spec/unit/mutant/mutator/node/begin_spec.rb +0 -32
  106. data/spec/unit/mutant/mutator/node/binary_spec.rb +0 -41
  107. data/spec/unit/mutant/mutator/node/block_pass_spec.rb +0 -15
  108. data/spec/unit/mutant/mutator/node/block_spec.rb +0 -83
  109. data/spec/unit/mutant/mutator/node/blockarg_spec.rb +0 -17
  110. data/spec/unit/mutant/mutator/node/case_spec.rb +0 -329
  111. data/spec/unit/mutant/mutator/node/cbase_spec.rb +0 -15
  112. data/spec/unit/mutant/mutator/node/conditional_loop_spec.rb +0 -58
  113. data/spec/unit/mutant/mutator/node/const_spec.rb +0 -16
  114. data/spec/unit/mutant/mutator/node/define_spec.rb +0 -171
  115. data/spec/unit/mutant/mutator/node/defined_spec.rb +0 -14
  116. data/spec/unit/mutant/mutator/node/dstr_spec.rb +0 -17
  117. data/spec/unit/mutant/mutator/node/dsym_spec.rb +0 -18
  118. data/spec/unit/mutant/mutator/node/ensure_spec.rb +0 -16
  119. data/spec/unit/mutant/mutator/node/if_spec.rb +0 -77
  120. data/spec/unit/mutant/mutator/node/kwbegin_spec.rb +0 -16
  121. data/spec/unit/mutant/mutator/node/literal/array_spec.rb +0 -47
  122. data/spec/unit/mutant/mutator/node/literal/boolean_spec.rb +0 -25
  123. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +0 -13
  124. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +0 -53
  125. data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +0 -33
  126. data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +0 -10
  127. data/spec/unit/mutant/mutator/node/literal/range_spec.rb +0 -56
  128. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +0 -36
  129. data/spec/unit/mutant/mutator/node/literal/string_spec.rb +0 -15
  130. data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +0 -15
  131. data/spec/unit/mutant/mutator/node/loop_ctrl_spec.rb +0 -37
  132. data/spec/unit/mutant/mutator/node/masgn_spec.rb +0 -14
  133. data/spec/unit/mutant/mutator/node/match_current_line_spec.rb +0 -21
  134. data/spec/unit/mutant/mutator/node/named_value/access_spec.rb +0 -78
  135. data/spec/unit/mutant/mutator/node/named_value/constant_assignment_spec.rb +0 -16
  136. data/spec/unit/mutant/mutator/node/named_value/variable_assignment_spec.rb +0 -61
  137. data/spec/unit/mutant/mutator/node/nthref_spec.rb +0 -19
  138. data/spec/unit/mutant/mutator/node/op_assgn_spec.rb +0 -22
  139. data/spec/unit/mutant/mutator/node/or_asgn_spec.rb +0 -19
  140. data/spec/unit/mutant/mutator/node/redo_spec.rb +0 -10
  141. data/spec/unit/mutant/mutator/node/rescue_spec.rb +0 -63
  142. data/spec/unit/mutant/mutator/node/restarg_spec.rb +0 -18
  143. data/spec/unit/mutant/mutator/node/return_spec.rb +0 -31
  144. data/spec/unit/mutant/mutator/node/send_spec.rb +0 -382
  145. data/spec/unit/mutant/mutator/node/super_spec.rb +0 -46
  146. data/spec/unit/mutant/mutator/node/yield_spec.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5ed19319f31c14a3b2677f67cda130f8ae053af1
4
- data.tar.gz: 6cb628e6e446ed9d1f0c14ad88cfd254d49ee216
3
+ metadata.gz: e86610168e1a64b920f694f5854137186fb808e0
4
+ data.tar.gz: e33eeafaebdb1a32042f56e03ed0794e25056f16
5
5
  SHA512:
6
- metadata.gz: 482eeee0d39d0e9b869b6a0625d71bf7b08bbdf8acff2fc9f29b44a9b74472cd038e166f053e17323dd41d46295f802245db69d7bb6dc7085336243bc448351b
7
- data.tar.gz: f332afc9b4404fbcb8ee20e5e74fe6789df531c856d1fc4562d5cb92dee1d771d900405ba044f5360bad39f6400002786584da7f0f4c6efce0be2c5bd4ba5f64
6
+ metadata.gz: 6237bcc40f7b53097546148fe68488f7b8af9e7c5e9b348cc3fa2811a3fb860f9b92b551ad3daf7aed87158061cc0ade88e894154352f54c6b50a2b0effee5d9
7
+ data.tar.gz: 42a09a5d0011978606d9ee12fae5bc1b9d4f2919a11aa783dcdc088f22127edd99fbb66a42c1ef7ab2c04efbc842b15c2e8f13af0ffa33ae3c481059ef4495b6
data/Changelog.md CHANGED
@@ -1,3 +1,9 @@
1
+ # v0.5.18 2014-06-04
2
+
3
+ Changes:
4
+
5
+ * Do not rename lhs of or assigns when lhs is a ivasgn. Closes #150
6
+
1
7
  # v0.5.17 2014-05-27
2
8
 
3
9
  Changes:
data/Rakefile CHANGED
@@ -15,3 +15,16 @@ if ENV['TRAVIS']
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ desc 'Generate mutation operator listing'
20
+ task :list do
21
+ require 'mutant'
22
+ # TODO: Add a nice public interface
23
+ registry = Mutant::Mutator::Registry.send(:registry)
24
+ registry.keys.select do |key|
25
+ key.is_a?(Symbol)
26
+ end.sort.each do |type|
27
+ mutator = registry.fetch(type)
28
+ puts '%-18s: %s' % [type, mutator.name.sub(/\AMutant::Mutator::Node::/, '')]
29
+ end
30
+ end
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 801
3
+ total_score: 849
data/config/reek.yml CHANGED
@@ -10,7 +10,8 @@ ClassVariable:
10
10
  exclude: []
11
11
  ControlParameter:
12
12
  enabled: true
13
- exclude: []
13
+ exclude:
14
+ - Mutant::Expression#match_length
14
15
  DataClump:
15
16
  enabled: true
16
17
  exclude: []
@@ -33,6 +34,8 @@ FeatureEnvy:
33
34
  # Nature of OptionParser :(
34
35
  - Mutant::CLI#add_environmental_options
35
36
  - Mutant::CLI#parse
37
+ # False positive, its a utility
38
+ - Mutant::Meta::Example::Verification#format_mutation
36
39
  IrresponsibleModule:
37
40
  enabled: true
38
41
  exclude: []
@@ -69,6 +72,7 @@ RepeatedConditional:
69
72
  - Mutant::Mutator
70
73
  - Mutant::Rspec::Strategy
71
74
  - Mutant::Reporter::CLI
75
+ - Mutant::Meta::Example::DSL
72
76
  max_ifs: 1
73
77
  TooManyInstanceVariables:
74
78
  enabled: true
@@ -94,6 +98,8 @@ TooManyStatements:
94
98
  - Mutant::Reporter::CLI#colorized_diff
95
99
  - Mutant::Reporter::CLI::Report::Config#run
96
100
  - Mutant::Runner#visit_collection
101
+ - Mutant::Runner#initialize
102
+ - Mutant::Runner::Mutation#run
97
103
  - Mutant::Zombifier::File#self.find
98
104
  - Mutant::RequireHighjack#infect
99
105
  - Mutant::Reporter::CLI::Registry#included
@@ -152,4 +158,5 @@ UtilityFunction:
152
158
  - Mutant::Rspec::Strategy#options
153
159
  - Mutant::Rspec::Strategy#world
154
160
  - Mutant::Rspec::Strategy#rspec2
161
+ - Mutant::Meta::Example::Verification#format_mutation
155
162
  max_helper_calls: 0
data/lib/mutant.rb CHANGED
@@ -26,6 +26,20 @@ module Mutant
26
26
  # The frozen empty array used within mutant
27
27
  EMPTY_ARRAY = [].freeze
28
28
 
29
+ # Lookup constant for location
30
+ #
31
+ # @param [String] location
32
+ #
33
+ # @return [Object]
34
+ #
35
+ # @api private
36
+ #
37
+ def self.constant_lookup(location)
38
+ location.sub(CBASE_PATTERN, EMPTY_STRING).split(SCOPE_OPERATOR).reduce(Object) do |parent, name|
39
+ parent.const_get(name, nil)
40
+ end
41
+ end
42
+
29
43
  # Perform self zombification
30
44
  #
31
45
  # @return [self]
@@ -144,6 +158,7 @@ require 'mutant/mutator/node/named_value/constant_assignment'
144
158
  require 'mutant/mutator/node/named_value/variable_assignment'
145
159
  require 'mutant/mutator/node/loop_control'
146
160
  require 'mutant/mutator/node/noop'
161
+ require 'mutant/mutator/node/or_asgn'
147
162
  require 'mutant/mutator/node/op_asgn'
148
163
  require 'mutant/mutator/node/conditional_loop'
149
164
  require 'mutant/mutator/node/yield'
@@ -186,6 +201,9 @@ require 'mutant/matcher/namespace'
186
201
  require 'mutant/matcher/scope'
187
202
  require 'mutant/matcher/filter'
188
203
  require 'mutant/matcher/null'
204
+ require 'mutant/expression'
205
+ require 'mutant/expression/method'
206
+ require 'mutant/expression/namespace'
189
207
  require 'mutant/killer'
190
208
  require 'mutant/test'
191
209
  require 'mutant/strategy'
@@ -195,9 +213,6 @@ require 'mutant/runner/subject'
195
213
  require 'mutant/runner/mutation'
196
214
  require 'mutant/runner/killer'
197
215
  require 'mutant/cli'
198
- require 'mutant/cli/classifier'
199
- require 'mutant/cli/classifier/namespace'
200
- require 'mutant/cli/classifier/method'
201
216
  require 'mutant/color'
202
217
  require 'mutant/diff'
203
218
  require 'mutant/reporter'
data/lib/mutant/cli.rb CHANGED
@@ -249,8 +249,11 @@ module Mutant
249
249
  #
250
250
  def parse_matchers(patterns)
251
251
  patterns.each do |pattern|
252
- matcher = Classifier.run(@cache, pattern)
253
- @builder.add_matcher(matcher)
252
+ expression = Expression.parse(pattern)
253
+ unless expression
254
+ raise Error, "Invalid mutant expression: #{pattern.inspect}"
255
+ end
256
+ @builder.add_matcher(expression.matcher(@cache))
254
257
  end
255
258
  end
256
259
 
@@ -321,7 +324,7 @@ module Mutant
321
324
  #
322
325
  def add_filter_options(opts)
323
326
  opts.on('--ignore-subject PATTERN', 'Ignore subjects that match PATTERN') do |pattern|
324
- @builder.add_subject_ignore(Classifier.run(@cache, pattern))
327
+ @builder.add_subject_ignore(Expression.parse(pattern).matcher(@cache))
325
328
  end
326
329
  opts.on('--code CODE', 'Scope execution to subjects with CODE') do |code|
327
330
  @builder.add_subject_selector(Morpher.compile(s(:eql, s(:attribute, :code), s(:static, code))))
@@ -4,6 +4,9 @@ module Mutant
4
4
 
5
5
  symbolset = ->(strings) { strings.map(&:to_sym).to_set.freeze }
6
6
 
7
+ SCOPE_OPERATOR = '::'.freeze
8
+ CBASE_PATTERN = /\A#{SCOPE_OPERATOR}/.freeze
9
+
7
10
  # Set of nodes that cannot be on the LHS of an assignment
8
11
  NOT_ASSIGNABLE = symbolset.(%w[int float str dstr class module self])
9
12
 
@@ -0,0 +1,110 @@
1
+ module Mutant
2
+
3
+ # Abstract base class for match expression
4
+ class Expression
5
+ include AbstractType, Adamantium, Concord::Public.new(:match)
6
+
7
+ include Equalizer.new(:syntax)
8
+
9
+ SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z\d_]*/.freeze
10
+
11
+ METHOD_NAME_PATTERN = Regexp.union(
12
+ /[A-Za-z_][A-Za-z\d_]*[!?=]?/,
13
+ *OPERATOR_METHODS.map(&:to_s)
14
+ ).freeze
15
+
16
+ SCOPE_PATTERN = /
17
+ (?:#{SCOPE_OPERATOR})?#{SCOPE_NAME_PATTERN}
18
+ (?:#{SCOPE_OPERATOR}#{SCOPE_NAME_PATTERN})*
19
+ /x.freeze
20
+
21
+ REGISTRY = {}
22
+
23
+ # Initialize expression
24
+ #
25
+ # @param [MatchData] match
26
+ def initialize(*)
27
+ super
28
+ @syntax = match.to_s
29
+ end
30
+
31
+ # Return syntax
32
+ #
33
+ # @return [String]
34
+ #
35
+ # @api private
36
+ #
37
+ attr_reader :syntax
38
+
39
+ # Return match length for expression
40
+ #
41
+ # @param [Expression] neddle
42
+ #
43
+ # @return [Fixnum]
44
+ #
45
+ def match_length(neddle)
46
+ if eql?(neddle)
47
+ syntax.length
48
+ else
49
+ 0
50
+ end
51
+ end
52
+
53
+ # Register expression
54
+ #
55
+ # @return [undefined]
56
+ #
57
+ # @api private
58
+ #
59
+ def self.register(regexp)
60
+ REGISTRY[regexp] = self
61
+ end
62
+ private_class_method :register
63
+
64
+ # Parse input into expression
65
+ #
66
+ # @param [String] pattern
67
+ #
68
+ # @return [Expression]
69
+ # if expression is valid
70
+ #
71
+ # @return [nil]
72
+ # otherwise
73
+ #
74
+ # @api private
75
+ #
76
+ def self.parse(pattern)
77
+ expressions = expressions(pattern)
78
+ case expressions.length
79
+ when 0
80
+ when 1
81
+ expressions.first
82
+ else
83
+ fail "Ambigous expression: #{pattern.inspect}"
84
+ end
85
+ end
86
+
87
+ # Return expressions for input
88
+ #
89
+ # @param [String] input
90
+ #
91
+ # @return [Classifier]
92
+ # if classifier can be found
93
+ #
94
+ # @return [nil]
95
+ # otherwise
96
+ #
97
+ # @api private
98
+ #
99
+ def self.expressions(input)
100
+ REGISTRY.each_with_object([]) do |(regexp, klass), expressions|
101
+ match = regexp.match(input)
102
+ if match
103
+ expressions << klass.new(match)
104
+ end
105
+ end
106
+ end
107
+ private_class_method :expressions
108
+
109
+ end # Expression
110
+ end # Mutant
@@ -0,0 +1,92 @@
1
+ module Mutant
2
+ class Expression
3
+
4
+ # Explicit method expression
5
+ class Method < self
6
+
7
+ MATCHERS = {
8
+ '.' => Matcher::Methods::Singleton,
9
+ '#' => Matcher::Methods::Instance
10
+ }.freeze
11
+
12
+ regexp = /
13
+ \A
14
+ (?<scope_name>#{SCOPE_PATTERN})
15
+ (?<scope_symbol>[.#])
16
+ (?<method_name>#{METHOD_NAME_PATTERN})
17
+ \z
18
+ /x.freeze
19
+
20
+ register(regexp)
21
+
22
+ # Return method matcher
23
+ #
24
+ # @param [Cache] cache
25
+ #
26
+ # @return [Matcher::Method]
27
+ #
28
+ # @api private
29
+ #
30
+ def matcher(cache)
31
+ methods_matcher = MATCHERS.fetch(scope_symbol).new(cache, scope)
32
+ method = methods_matcher.methods.detect do |meth|
33
+ meth.name == method_name
34
+ end or raise NameError, "Cannot find method #{identifier}"
35
+ methods_matcher.matcher.build(cache, scope, method)
36
+ end
37
+
38
+ private
39
+
40
+ # Return scope name
41
+ #
42
+ # @return [String]
43
+ #
44
+ # @api private
45
+ #
46
+ def scope_name
47
+ match[__method__]
48
+ end
49
+
50
+ # Return scope
51
+ #
52
+ # @return [Class, Method]
53
+ #
54
+ # @api private
55
+ #
56
+ def scope
57
+ Mutant.constant_lookup(scope_name)
58
+ end
59
+
60
+ # Return method name
61
+ #
62
+ # @return [String]
63
+ #
64
+ # @api private
65
+ #
66
+ def method_name
67
+ match[__method__].to_sym
68
+ end
69
+
70
+ # Return scope symbol
71
+ #
72
+ # @return [Symbol]
73
+ #
74
+ # @api private
75
+ #
76
+ def scope_symbol
77
+ match[__method__]
78
+ end
79
+
80
+ # Return matcher class
81
+ #
82
+ # @return [Class:Mutant::Matcher]
83
+ #
84
+ # @api private
85
+ #
86
+ def methods_matcher(cache)
87
+ MATCHERS.fetch(scope_symbol).new(cache, scope)
88
+ end
89
+
90
+ end # Method
91
+ end # Expression
92
+ end # Mutant
@@ -0,0 +1,90 @@
1
+ module Mutant
2
+ class Expression
3
+ # Abstract base class for expressions matching namespaces
4
+ class Namespace < self
5
+ include AbstractType
6
+
7
+ # Return matcher
8
+ #
9
+ # @param [Cache] cache
10
+ #
11
+ # @return [Matcher]
12
+ #
13
+ # @api private
14
+ #
15
+ def matcher(cache)
16
+ self.class::MATCHER.new(cache, namespace)
17
+ end
18
+
19
+ private
20
+
21
+ # Return namespace
22
+ #
23
+ # @return [Class, Module]
24
+ #
25
+ # @api private
26
+ #
27
+ def namespace
28
+ Mutant.constant_lookup(match[__method__].to_s)
29
+ end
30
+
31
+ # Recursive namespace expression
32
+ class Recursive < self
33
+
34
+ register(/\A(?<namespace>#{SCOPE_PATTERN})?\*\z/)
35
+
36
+ MATCHER = Matcher::Namespace
37
+
38
+ # Initialize object
39
+ #
40
+ # @return [undefined]
41
+ #
42
+ # @api private
43
+ def initialize(*)
44
+ super
45
+ namespace_src = Regexp.escape(namespace)
46
+ @recursion_pattern = Regexp.union(/\A#{namespace_src}\z/, /\A#{namespace_src}::/)
47
+ end
48
+
49
+ # Return length of match
50
+ #
51
+ # @param [Expression] expression
52
+ #
53
+ # @return [Fixnum]
54
+ #
55
+ # @api private
56
+ #
57
+ def match_length(expression)
58
+ if @recursion_pattern =~ expression.syntax
59
+ namespace.length
60
+ else
61
+ 0
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Return matched namespace
68
+ #
69
+ # @return [String]
70
+ #
71
+ # @api private
72
+ #
73
+ def namespace
74
+ match[__method__] || ''
75
+ end
76
+
77
+ end # Recursive
78
+
79
+ # Exact namespace expression
80
+ class Exact < self
81
+
82
+ register(/\A(?<namespace>#{SCOPE_PATTERN})\z/)
83
+
84
+ MATCHER = Matcher::Scope
85
+
86
+ end # Exact
87
+
88
+ end # Namespace
89
+ end # Namespace
90
+ end # Mutant