Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10217 from mttkay/mk-avoid-openstruct
Add Style/OpenStructUse Cop
- Loading branch information
Showing
11 changed files
with
194 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* [#10217](https://github.com/rubocop/rubocop/pull/10217): Add new `Style/OpenStructUse` cop. ([@mttkay][]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module Style | ||
# This cop flags uses of OpenStruct, as it is now officially discouraged | ||
# to be used for performance, version compatibility, and potential security issues. | ||
# | ||
# @safety | ||
# | ||
# Note that this cop may flag false positives; for instance, the following legal | ||
# use of a hand-rolled `OpenStruct` type would be considered an offense: | ||
# | ||
# ``` | ||
# module MyNamespace | ||
# class OpenStruct # not the OpenStruct we're looking for | ||
# end | ||
# | ||
# def new_struct | ||
# OpenStruct.new # resolves to MyNamespace::OpenStruct | ||
# end | ||
# end | ||
# ``` | ||
# | ||
# @example | ||
# | ||
# # bad | ||
# point = OpenStruct.new(x: 0, y: 1) | ||
# | ||
# # good | ||
# Point = Struct.new(:x, :y) | ||
# point = Point.new(0, 1) | ||
# | ||
# # also good | ||
# point = { x: 0, y: 1 } | ||
# | ||
# # bad | ||
# test_double = OpenStruct.new(a: 'b') | ||
# | ||
# # good (assumes test using rspec-mocks) | ||
# test_double = double | ||
# allow(test_double).to receive(:a).and_return('b') | ||
# | ||
class OpenStructUse < Base | ||
MSG = 'Avoid using `OpenStruct`; use `Struct`, `Hash`, a class or test doubles instead.' | ||
|
||
# @!method uses_open_struct?(node) | ||
def_node_matcher :uses_open_struct?, <<-PATTERN | ||
(const {nil? (cbase)} :OpenStruct) | ||
PATTERN | ||
|
||
def on_const(node) | ||
return unless uses_open_struct?(node) | ||
return if custom_class_or_module_definition?(node) | ||
|
||
add_offense(node) | ||
end | ||
|
||
private | ||
|
||
def custom_class_or_module_definition?(node) | ||
parent = node.parent | ||
|
||
(parent.class_type? || parent.module_type?) && node.left_siblings.empty? | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::Cop::Style::OpenStructUse, :config do | ||
context 'when using OpenStruct' do | ||
['OpenStruct', '::OpenStruct'].each do |klass| | ||
context "for #{klass}" do | ||
context 'when used in assignments' do | ||
it 'registers an offense' do | ||
expect_offense(<<~RUBY, klass: klass) | ||
a = %{klass}.new(a: 42) | ||
^{klass} Avoid using `OpenStruct`;[...] | ||
RUBY | ||
end | ||
end | ||
|
||
context 'when inheriting from it via <' do | ||
it 'registers an offense' do | ||
expect_offense(<<~RUBY, klass: klass) | ||
class SubClass < %{klass} | ||
^{klass} Avoid using `OpenStruct`;[...] | ||
end | ||
RUBY | ||
end | ||
end | ||
|
||
context 'when inheriting from it via Class.new' do | ||
it 'registers an offense' do | ||
expect_offense(<<~RUBY, klass: klass) | ||
SubClass = Class.new(%{klass}) | ||
^{klass} Avoid using `OpenStruct`;[...] | ||
RUBY | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
context 'when using custom namespaced OpenStruct' do | ||
context 'when inheriting from it' do | ||
specify { expect_no_offenses('class A < SomeNamespace::OpenStruct; end') } | ||
end | ||
|
||
context 'when defined in custom namespace' do | ||
context 'when class' do | ||
specify do | ||
expect_no_offenses(<<~RUBY) | ||
module SomeNamespace | ||
class OpenStruct | ||
end | ||
end | ||
RUBY | ||
end | ||
end | ||
|
||
context 'when module' do | ||
specify do | ||
expect_no_offenses(<<~RUBY) | ||
module SomeNamespace | ||
module OpenStruct | ||
end | ||
end | ||
RUBY | ||
end | ||
end | ||
end | ||
|
||
context 'when used in assignments' do | ||
it 'registers no offense' do | ||
expect_no_offenses(<<~RUBY) | ||
a = SomeNamespace::OpenStruct.new | ||
RUBY | ||
end | ||
end | ||
end | ||
|
||
context 'when not using OpenStruct' do | ||
it 'registers no offense', :aggregate_failures do | ||
expect_no_offenses('class A < B; end') | ||
expect_no_offenses('a = 42') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# frozen_string_literal: true | ||
|
||
# Tests may use this to fake out a location structure in an Offense. | ||
FakeLocation = Struct.new(:line, :column, keyword_init: true) |