Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #9716] Support deprecated Socket.gethostbyaddr and Socket.gethostbyname #9732

Merged
merged 3 commits into from May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/fix_support_deprecated_socket.md
@@ -0,0 +1 @@
* [#9732](https://github.com/rubocop/rubocop/pull/9732): Support deprecated Socket.gethostbyaddr and Socket.gethostbyname. ([@AndreiEres][])
118 changes: 79 additions & 39 deletions lib/rubocop/cop/lint/deprecated_class_methods.rb
Expand Up @@ -24,17 +24,17 @@ class DeprecatedClassMethods < Base
extend AutoCorrector

# Inner class to DeprecatedClassMethods.
# This class exists to add abstraction and clean naming to the
# objects that are going to be operated on.
# This class exists to add abstraction and clean naming
# to the deprecated objects
class DeprecatedClassMethod
include RuboCop::AST::Sexp

attr_reader :class_constant, :deprecated_method, :replacement_method
attr_reader :method, :class_constant

def initialize(deprecated:, replacement:, class_constant: nil)
@deprecated_method = deprecated
@replacement_method = replacement
def initialize(method, class_constant: nil, correctable: true)
@method = method
@class_constant = class_constant
@correctable = correctable
end

def class_nodes
Expand All @@ -48,57 +48,97 @@ def class_nodes
[nil]
end
end

def correctable?
@correctable
end

def to_s
[class_constant, method].compact.join(delimeter)
end

private

def delimeter
CLASS_METHOD_DELIMETER
end
end

# Inner class to DeprecatedClassMethods.
# This class exists to add abstraction and clean naming
# to the replacements for deprecated objects
class Replacement
attr_reader :method, :class_constant

def initialize(method, class_constant: nil, instance_method: false)
@method = method
@class_constant = class_constant
@instance_method = instance_method
end

def to_s
[class_constant, method].compact.join(delimeter)
end

private

def delimeter
instance_method? ? INSTANCE_METHOD_DELIMETER : CLASS_METHOD_DELIMETER
end

def instance_method?
@instance_method
end
end

MSG = '`%<current>s` is deprecated in favor of `%<prefer>s`.'
DEPRECATED_METHODS_OBJECT = [
DeprecatedClassMethod.new(deprecated: :exists?,
replacement: :exist?,
class_constant: :File),
DeprecatedClassMethod.new(deprecated: :exists?,
replacement: :exist?,
class_constant: :Dir),
DeprecatedClassMethod.new(deprecated: :iterator?, replacement: :block_given?)
].freeze

RESTRICT_ON_SEND = DEPRECATED_METHODS_OBJECT.map(&:deprecated_method).freeze

DEPRECATED_METHODS_OBJECT = {
DeprecatedClassMethod.new(:exists?, class_constant: :File) =>
Replacement.new(:exist?, class_constant: :File),

DeprecatedClassMethod.new(:exists?, class_constant: :Dir) =>
Replacement.new(:exist?, class_constant: :Dir),

DeprecatedClassMethod.new(:iterator?) => Replacement.new(:block_given?),

DeprecatedClassMethod.new(:gethostbyaddr, class_constant: :Socket, correctable: false) =>
Replacement.new(:getnameinfo, class_constant: :Addrinfo, instance_method: true),

DeprecatedClassMethod.new(:gethostbyname, class_constant: :Socket, correctable: false) =>
Replacement.new(:getaddrinfo, class_constant: :Addrinfo, instance_method: true)
}.freeze

RESTRICT_ON_SEND = DEPRECATED_METHODS_OBJECT.keys.map(&:method).freeze

CLASS_METHOD_DELIMETER = '.'
INSTANCE_METHOD_DELIMETER = '#'

def on_send(node)
check(node) do |data|
message = format(MSG, current: deprecated_method(data),
prefer: replacement_method(data))
check(node) do |deprecated|
message = format(MSG, current: deprecated, prefer: replacement(deprecated))

add_offense(node.loc.selector, message: message) do |corrector|
corrector.replace(node.loc.selector, data.replacement_method.to_s)
if deprecated.correctable?
corrector.replace(node.loc.selector, replacement(deprecated).method)
end
end
end
end

private

def check(node)
DEPRECATED_METHODS_OBJECT.each do |data|
next unless data.class_nodes.include?(node.receiver)
next unless node.method?(data.deprecated_method)
DEPRECATED_METHODS_OBJECT.each_key do |deprecated|
next unless deprecated.class_nodes.include?(node.receiver)
next unless node.method?(deprecated.method)

yield data
yield deprecated
end
end

def deprecated_method(data)
method_call(data.class_constant, data.deprecated_method)
end

def replacement_method(data)
method_call(data.class_constant, data.replacement_method)
end

def method_call(class_constant, method)
if class_constant
format('%<constant>s.%<method>s', constant: class_constant, method: method)
else
format('%<method>s', method: method)
end
def replacement(deprecated)
DEPRECATED_METHODS_OBJECT[deprecated]
end
end
end
Expand Down
48 changes: 48 additions & 0 deletions spec/rubocop/cop/lint/deprecated_class_methods_spec.rb
Expand Up @@ -81,4 +81,52 @@
expect_no_offenses('Foo.iterator?')
end
end

context 'prefer `Addrinfo#getnameinfo` over `Socket.gethostbyaddr`' do
it 'registers an offense for Socket.gethostbyaddr' do
expect_offense(<<~RUBY)
Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
^^^^^^^^^^^^^ `Socket.gethostbyaddr` is deprecated in favor of `Addrinfo#getnameinfo`.
RUBY

expect_no_corrections
end

it 'registers an offense for ::Socket.gethostbyaddr' do
expect_offense(<<~RUBY)
::Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
^^^^^^^^^^^^^ `Socket.gethostbyaddr` is deprecated in favor of `Addrinfo#getnameinfo`.
RUBY

expect_no_corrections
end

it 'does not register an offense for method `gethostbyaddr` on other receivers' do
expect_no_offenses('Foo.gethostbyaddr')
end
end

context 'prefer `Addrinfo#getaddrinfo` over `Socket.gethostbyname`' do
it 'registers an offense for Socket.gethostbyname' do
expect_offense(<<~RUBY)
Socket.gethostbyname("hal")
^^^^^^^^^^^^^ `Socket.gethostbyname` is deprecated in favor of `Addrinfo#getaddrinfo`.
RUBY

expect_no_corrections
end

it 'registers an offense for ::Socket.gethostbyname' do
expect_offense(<<~RUBY)
::Socket.gethostbyname("hal")
^^^^^^^^^^^^^ `Socket.gethostbyname` is deprecated in favor of `Addrinfo#getaddrinfo`.
RUBY

expect_no_corrections
end

it 'does not register an offense for method `gethostbyname` on other receivers' do
expect_no_offenses('Foo.gethostbyname')
end
end
end