This file is indexed.

/usr/lib/ruby/vendor_ruby/rspec/support/method_signature_verifier.rb is in ruby-rspec-support 3.5.0c3e0m0s0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
require 'rspec/support'
RSpec::Support.require_rspec_support "ruby_features"
RSpec::Support.require_rspec_support "matcher_definition"

module RSpec
  module Support
    # Extracts info about the number of arguments and allowed/required
    # keyword args of a given method.
    #
    # @private
    class MethodSignature # rubocop:disable ClassLength
      attr_reader :min_non_kw_args, :max_non_kw_args, :optional_kw_args, :required_kw_args

      def initialize(method)
        @method           = method
        @optional_kw_args = []
        @required_kw_args = []
        classify_parameters
      end

      def non_kw_args_arity_description
        case max_non_kw_args
        when min_non_kw_args then min_non_kw_args.to_s
        when INFINITY then "#{min_non_kw_args} or more"
        else "#{min_non_kw_args} to #{max_non_kw_args}"
        end
      end

      def valid_non_kw_args?(positional_arg_count, optional_max_arg_count=positional_arg_count)
        return true if positional_arg_count.nil?

        min_non_kw_args <= positional_arg_count &&
          optional_max_arg_count <= max_non_kw_args
      end

      if RubyFeatures.optional_and_splat_args_supported?
        def description
          @description ||= begin
            parts = []

            unless non_kw_args_arity_description == "0"
              parts << "arity of #{non_kw_args_arity_description}"
            end

            if @optional_kw_args.any?
              parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})"
            end

            if @required_kw_args.any?
              parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})"
            end

            parts << "any additional keyword args" if @allows_any_kw_args

            parts.join(" and ")
          end
        end

        def missing_kw_args_from(given_kw_args)
          @required_kw_args - given_kw_args
        end

        def invalid_kw_args_from(given_kw_args)
          return [] if @allows_any_kw_args
          given_kw_args - @allowed_kw_args
        end

        def has_kw_args_in?(args)
          Hash === args.last && could_contain_kw_args?(args)
        end

        # Without considering what the last arg is, could it
        # contain keyword arguments?
        def could_contain_kw_args?(args)
          return false if args.count <= min_non_kw_args
          @allows_any_kw_args || @allowed_kw_args.any?
        end

        def arbitrary_kw_args?
          @allows_any_kw_args
        end

        def unlimited_args?
          @max_non_kw_args == INFINITY
        end

        def classify_parameters
          optional_non_kw_args = @min_non_kw_args = 0
          @allows_any_kw_args = false

          @method.parameters.each do |(type, name)|
            case type
            # def foo(a:)
            when :keyreq  then @required_kw_args << name
            # def foo(a: 1)
            when :key     then @optional_kw_args << name
            # def foo(**kw_args)
            when :keyrest then @allows_any_kw_args = true
            # def foo(a)
            when :req     then @min_non_kw_args += 1
            # def foo(a = 1)
            when :opt     then optional_non_kw_args += 1
            # def foo(*a)
            when :rest    then optional_non_kw_args = INFINITY
            end
          end

          @max_non_kw_args = @min_non_kw_args  + optional_non_kw_args
          @allowed_kw_args = @required_kw_args + @optional_kw_args
        end
      else
        def description
          "arity of #{non_kw_args_arity_description}"
        end

        def missing_kw_args_from(_given_kw_args)
          []
        end

        def invalid_kw_args_from(_given_kw_args)
          []
        end

        def has_kw_args_in?(_args)
          false
        end

        def could_contain_kw_args?(*)
          false
        end

        def arbitrary_kw_args?
          false
        end

        def unlimited_args?
          false
        end

        def classify_parameters
          arity = @method.arity
          if arity < 0
            # `~` inverts the one's complement and gives us the
            # number of required args
            @min_non_kw_args = ~arity
            @max_non_kw_args = INFINITY
          else
            @min_non_kw_args = arity
            @max_non_kw_args = arity
          end
        end
      end

      INFINITY = 1 / 0.0
    end

    # Some versions of JRuby have a nasty bug we have to work around :(.
    # https://github.com/jruby/jruby/issues/2816
    if RSpec::Support::Ruby.jruby? &&
       RubyFeatures.optional_and_splat_args_supported? &&
       Class.new { attr_writer :foo }.instance_method(:foo=).parameters == []

      class MethodSignature < remove_const(:MethodSignature)
      private

        def classify_parameters
          super
          return unless @method.parameters == [] && @method.arity == 1
          @max_non_kw_args = @min_non_kw_args = 1
        end
      end
    end

    # Encapsulates expectations about the number of arguments and
    # allowed/required keyword args of a given method.
    #
    # @api private
    class MethodSignatureExpectation
      def initialize
        @min_count = nil
        @max_count = nil
        @keywords  = []

        @expect_unlimited_arguments = false
        @expect_arbitrary_keywords  = false
      end

      attr_reader :min_count, :max_count, :keywords

      attr_accessor :expect_unlimited_arguments, :expect_arbitrary_keywords

      def max_count=(number)
        raise ArgumentError, 'must be a non-negative integer or nil' \
          unless number.nil? || (number.is_a?(Integer) && number >= 0)

        @max_count = number
      end

      def min_count=(number)
        raise ArgumentError, 'must be a non-negative integer or nil' \
          unless number.nil? || (number.is_a?(Integer) && number >= 0)

        @min_count = number
      end

      def empty?
        @min_count.nil? &&
          @keywords.to_a.empty? &&
          !@expect_arbitrary_keywords &&
          !@expect_unlimited_arguments
      end

      def keywords=(values)
        @keywords = values.to_a || []
      end
    end

    # Deals with the slightly different semantics of block arguments.
    # For methods, arguments are required unless a default value is provided.
    # For blocks, arguments are optional, even if no default value is provided.
    #
    # However, we want to treat block args as required since you virtually
    # always want to pass a value for each received argument and our
    # `and_yield` has treated block args as required for many years.
    #
    # @api private
    class BlockSignature < MethodSignature
      if RubyFeatures.optional_and_splat_args_supported?
        def classify_parameters
          super
          @min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY
        end
      end
    end

    # Abstract base class for signature verifiers.
    #
    # @api private
    class MethodSignatureVerifier
      attr_reader :non_kw_args, :kw_args, :min_non_kw_args, :max_non_kw_args

      def initialize(signature, args=[])
        @signature = signature
        @non_kw_args, @kw_args = split_args(*args)
        @min_non_kw_args = @max_non_kw_args = @non_kw_args
        @arbitrary_kw_args = @unlimited_args = false
      end

      def with_expectation(expectation) # rubocop:disable MethodLength
        return self unless MethodSignatureExpectation === expectation

        if expectation.empty?
          @min_non_kw_args = @max_non_kw_args = @non_kw_args = nil
          @kw_args     = []
        else
          @min_non_kw_args = @non_kw_args = expectation.min_count || 0
          @max_non_kw_args                = expectation.max_count || @min_non_kw_args

          if RubyFeatures.optional_and_splat_args_supported?
            @unlimited_args = expectation.expect_unlimited_arguments
          else
            @unlimited_args = false
          end

          if RubyFeatures.kw_args_supported?
            @kw_args           = expectation.keywords
            @arbitrary_kw_args = expectation.expect_arbitrary_keywords
          else
            @kw_args           = []
            @arbitrary_kw_args = false
          end
        end

        self
      end

      def valid?
        missing_kw_args.empty? &&
          invalid_kw_args.empty? &&
          valid_non_kw_args? &&
          arbitrary_kw_args? &&
          unlimited_args?
      end

      def error_message
        if missing_kw_args.any?
          "Missing required keyword arguments: %s" % [
            missing_kw_args.join(", ")
          ]
        elsif invalid_kw_args.any?
          "Invalid keyword arguments provided: %s" % [
            invalid_kw_args.join(", ")
          ]
        elsif !valid_non_kw_args?
          "Wrong number of arguments. Expected %s, got %s." % [
            @signature.non_kw_args_arity_description,
            non_kw_args
          ]
        end
      end

    private

      def valid_non_kw_args?
        @signature.valid_non_kw_args?(min_non_kw_args, max_non_kw_args)
      end

      def missing_kw_args
        @signature.missing_kw_args_from(kw_args)
      end

      def invalid_kw_args
        @signature.invalid_kw_args_from(kw_args)
      end

      def arbitrary_kw_args?
        !@arbitrary_kw_args || @signature.arbitrary_kw_args?
      end

      def unlimited_args?
        !@unlimited_args || @signature.unlimited_args?
      end

      def split_args(*args)
        kw_args = if @signature.has_kw_args_in?(args)
                    args.pop.keys
                  else
                    []
                  end

        [args.length, kw_args]
      end
    end

    # Figures out wether a given method can accept various arguments.
    # Surprisingly non-trivial.
    #
    # @private
    StrictSignatureVerifier = MethodSignatureVerifier

    # Allows matchers to be used instead of providing keyword arguments. In
    # practice, when this happens only the arity of the method is verified.
    #
    # @private
    class LooseSignatureVerifier < MethodSignatureVerifier
    private

      def split_args(*args)
        if RSpec::Support.is_a_matcher?(args.last) && @signature.could_contain_kw_args?(args)
          args.pop
          @signature = SignatureWithKeywordArgumentsMatcher.new(@signature)
        end

        super(*args)
      end

      # If a matcher is used in a signature in place of keyword arguments, all
      # keyword argument validation needs to be skipped since the matcher is
      # opaque.
      #
      # Instead, keyword arguments will be validated when the method is called
      # and they are actually known.
      #
      # @private
      class SignatureWithKeywordArgumentsMatcher
        def initialize(signature)
          @signature = signature
        end

        def missing_kw_args_from(_kw_args)
          []
        end

        def invalid_kw_args_from(_kw_args)
          []
        end

        def non_kw_args_arity_description
          @signature.non_kw_args_arity_description
        end

        def valid_non_kw_args?(*args)
          @signature.valid_non_kw_args?(*args)
        end

        def has_kw_args_in?(args)
          @signature.has_kw_args_in?(args)
        end
      end
    end
  end
end