Skip to content

Commit

Permalink
Merge pull request #181 from Shopify/ko/aliased-shapes
Browse files Browse the repository at this point in the history
Forbid usage of type aliased shapes
  • Loading branch information
KaanOzkan authored Nov 24, 2023
2 parents 8f3126a + 5bb7bfe commit 6613f8e
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 0 deletions.
5 changes: 5 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ Sorbet/ForbidIncludeConstLiteral:
VersionAdded: 0.2.0
VersionChanged: 0.5.0

Sorbet/ForbidTypeAliasedShapes:
Description: 'Forbids defining type aliases that contain shapes'
Enabled: false
VersionAdded: 0.7.6

Sorbet/ForbidSuperclassConstLiteral:
Description: 'Forbid superclasses which are non-literal constants.'
Enabled: false
Expand Down
44 changes: 44 additions & 0 deletions lib/rubocop/cop/sorbet/forbid_type_aliased_shapes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require "rubocop"

module RuboCop
module Cop
module Sorbet
# Disallows defining type aliases that contain shapes
#
# @example
#
# # bad
# Foo = T.type_alias { { foo: Integer } }
#
# # good
# class Foo
# extend T::Sig
#
# sig { params(foo: Integer).void }
# def initialize(foo)
# @foo = foo
# end
# end
class ForbidTypeAliasedShapes < RuboCop::Cop::Base
MSG = "Type aliases shouldn't contain shapes because of significant performance overhead"

# @!method shape_type_alias?(node)
def_node_matcher(:shape_type_alias?, <<-PATTERN)
(block
(send (const {nil? cbase} :T) :type_alias)
(args)
`hash
)
PATTERN

def on_block(node)
add_offense(node) if shape_type_alias?(node)
end

alias_method :on_numblock, :on_block
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/sorbet_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative "sorbet/constants_from_strings"
require_relative "sorbet/forbid_superclass_const_literal"
require_relative "sorbet/forbid_include_const_literal"
require_relative "sorbet/forbid_type_aliased_shapes"
require_relative "sorbet/forbid_untyped_struct_props"
require_relative "sorbet/implicit_conversion_method"
require_relative "sorbet/one_ancestor_per_line"
Expand Down
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ In the following section you find all available cops:
* [Sorbet/ForbidTStruct](cops_sorbet.md#sorbetforbidtstruct)
* [Sorbet/ForbidTUnsafe](cops_sorbet.md#sorbetforbidtunsafe)
* [Sorbet/ForbidTUntyped](cops_sorbet.md#sorbetforbidtuntyped)
* [Sorbet/ForbidTypeAliasedShapes](cops_sorbet.md#sorbetforbidtypealiasedshapes)
* [Sorbet/ForbidUntypedStructProps](cops_sorbet.md#sorbetforbiduntypedstructprops)
* [Sorbet/HasSigil](cops_sorbet.md#sorbethassigil)
* [Sorbet/IgnoreSigil](cops_sorbet.md#sorbetignoresigil)
Expand Down
25 changes: 25 additions & 0 deletions manual/cops_sorbet.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,31 @@ sig { params(my_argument: String).void }
def foo(my_argument); end
```
## Sorbet/ForbidTypeAliasedShapes
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Disabled | Yes | No | 0.7.6 | -
Disallows defining type aliases that contain shapes
### Examples
```ruby
# bad
Foo = T.type_alias { { foo: Integer } }

# good
class Foo
extend T::Sig

sig { params(foo: Integer).void }
def initialize(foo)
@foo = foo
end
end
```
## Sorbet/ForbidUntypedStructProps
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
35 changes: 35 additions & 0 deletions spec/rubocop/cop/sorbet/forbid_type_aliased_shapes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe(RuboCop::Cop::Sorbet::ForbidTypeAliasedShapes, :config) do
def message
"Type aliases shouldn't contain shapes because of significant performance overhead"
end

it("allows defining type aliases that don't contain shapes") do
expect_no_offenses(<<~RUBY)
Foo = T.type_alias { Integer }
RUBY
end

it("disallows defining type aliases that contain shapes") do
expect_offense(<<~RUBY)
Foo = T.type_alias { { foo: Integer } }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY
end

it("disallows defining type aliases that contain nested shapes") do
expect_offense(<<~RUBY)
A = T.type_alias { [{ foo: Integer }] }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
B = T.type_alias { T.nilable({ foo: Integer }) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
C = T.type_alias { T::Hash[Symbol, { foo: Integer }] }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
D = T.type_alias { T::Hash[Symbol, T::Array[T.any(String, { foo: Integer })]] }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY
end
end

0 comments on commit 6613f8e

Please sign in to comment.