2020-11-23 07:09:11 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module RuboCop
|
|
|
|
module Cop
|
|
|
|
module Lint
|
|
|
|
# This cop only works if there are files from deprecation_toolkit. You can
|
|
|
|
# generate these files by:
|
|
|
|
#
|
|
|
|
# 1. Running specs with RECORD_DEPRECATIONS=1
|
|
|
|
# 1. Downloading the complete set of deprecations/ files from a CI
|
|
|
|
# pipeline (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47720)
|
|
|
|
class LastKeywordArgument < Cop
|
2021-03-23 11:09:28 -04:00
|
|
|
MSG = 'Using the last argument as keyword parameters is deprecated'
|
2020-11-23 07:09:11 -05:00
|
|
|
|
|
|
|
DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__)
|
|
|
|
KEYWORD_DEPRECATION_STR = 'maybe ** should be added to the call'
|
|
|
|
|
|
|
|
def on_send(node)
|
2021-01-12 04:10:49 -05:00
|
|
|
arg = get_last_argument(node)
|
2020-11-23 07:09:11 -05:00
|
|
|
return unless arg
|
|
|
|
|
|
|
|
return unless known_match?(processed_source.file_path, node.first_line, node.method_name.to_s)
|
|
|
|
|
|
|
|
return if arg.children.first.respond_to?(:kwsplat_type?) && arg.children.first&.kwsplat_type?
|
|
|
|
|
|
|
|
# parser thinks `a: :b, c: :d` is hash type, it's actually kwargs
|
|
|
|
return if arg.hash_type? && !arg.source.match(/\A{/)
|
|
|
|
|
|
|
|
add_offense(arg)
|
|
|
|
end
|
|
|
|
|
|
|
|
def autocorrect(arg)
|
|
|
|
lambda do |corrector|
|
|
|
|
if arg.hash_type?
|
|
|
|
kwarg = arg.source.sub(/\A{\s*/, '').sub(/\s*}\z/, '')
|
|
|
|
corrector.replace(arg, kwarg)
|
|
|
|
elsif arg.splat_type?
|
|
|
|
corrector.insert_before(arg, '*')
|
|
|
|
else
|
|
|
|
corrector.insert_before(arg, '**')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2021-01-12 04:10:49 -05:00
|
|
|
def get_last_argument(node)
|
|
|
|
return node.arguments[-2] if node.block_argument?
|
|
|
|
|
|
|
|
node.arguments.last
|
|
|
|
end
|
|
|
|
|
2020-11-23 07:09:11 -05:00
|
|
|
def known_match?(file_path, line_number, method_name)
|
|
|
|
file_path_from_root = file_path.sub(File.expand_path('../../..', __dir__), '')
|
2021-01-13 13:10:55 -05:00
|
|
|
file_and_line = "#{file_path_from_root}:#{line_number}"
|
2020-11-23 07:09:11 -05:00
|
|
|
|
2020-11-26 07:09:48 -05:00
|
|
|
method_name = 'initialize' if method_name == 'new'
|
|
|
|
|
2021-01-13 13:10:55 -05:00
|
|
|
return unless self.class.keyword_warnings[method_name]
|
|
|
|
|
|
|
|
self.class.keyword_warnings[method_name].any? do |warning|
|
|
|
|
warning.include?(file_and_line)
|
2020-11-23 07:09:11 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.keyword_warnings
|
|
|
|
@keyword_warnings ||= keywords_list
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.keywords_list
|
|
|
|
hash = Dir.glob(DEPRECATIONS_GLOB).each_with_object({}) do |file, hash|
|
|
|
|
hash.merge!(YAML.safe_load(File.read(file)))
|
|
|
|
end
|
|
|
|
|
2021-01-13 13:10:55 -05:00
|
|
|
hash.values.flatten.each_with_object({}) do |str, results|
|
|
|
|
next unless str.include?(KEYWORD_DEPRECATION_STR)
|
|
|
|
|
|
|
|
match_data = str.match(/called method `([^\s]+)'/)
|
|
|
|
next unless match_data
|
|
|
|
|
|
|
|
key = match_data[1]
|
|
|
|
results[key] ||= []
|
|
|
|
results[key] << str
|
|
|
|
end
|
2020-11-23 07:09:11 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|