Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ Sorbet/TrueSigil:

Sorbet/EnforceSigilOrder:
Enabled: true

Sorbet/ForbidTEnum:
Enabled: true
4 changes: 2 additions & 2 deletions lib/spoom/cli/deadcode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def deadcode(*paths)
index.definitions.each do |name, definitions|
$stderr.puts " #{blue(name)}"
definitions.each do |definition|
$stderr.puts " #{yellow(definition.kind.serialize)} #{gray(definition.location.to_s)}"
$stderr.puts " #{yellow(definition.kind.to_s)} #{gray(definition.location.to_s)}"
end
end
$stderr.puts
Expand All @@ -116,7 +116,7 @@ def deadcode(*paths)
$stderr.puts "\nReferences:"
index.references.values.flatten.sort_by(&:name).each do |references|
name = references.name
kind = references.kind.serialize
kind = references.kind.to_s
loc = references.location.to_s
$stderr.puts " #{blue(name)} #{yellow(kind)} #{gray(loc)}"
end
Expand Down
51 changes: 26 additions & 25 deletions lib/spoom/colors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,35 @@
# frozen_string_literal: true

module Spoom
class Color < T::Enum
enums do
CLEAR = new("\e[0m")
BOLD = new("\e[1m")
class Color
#: String
attr_reader :ansi_code

BLACK = new("\e[30m")
RED = new("\e[31m")
GREEN = new("\e[32m")
YELLOW = new("\e[33m")
BLUE = new("\e[34m")
MAGENTA = new("\e[35m")
CYAN = new("\e[36m")
WHITE = new("\e[37m")

LIGHT_BLACK = new("\e[90m")
LIGHT_RED = new("\e[91m")
LIGHT_GREEN = new("\e[92m")
LIGHT_YELLOW = new("\e[93m")
LIGHT_BLUE = new("\e[94m")
LIGHT_MAGENTA = new("\e[95m")
LIGHT_CYAN = new("\e[96m")
LIGHT_WHITE = new("\e[97m")
#: (String) -> void
def initialize(ansi_code)
@ansi_code = ansi_code
end

#: -> String
def ansi_code
serialize
end
CLEAR = new("\e[0m") #: Color
BOLD = new("\e[1m") #: Color

BLACK = new("\e[30m") #: Color
RED = new("\e[31m") #: Color
GREEN = new("\e[32m") #: Color
YELLOW = new("\e[33m") #: Color
BLUE = new("\e[34m") #: Color
MAGENTA = new("\e[35m") #: Color
CYAN = new("\e[36m") #: Color
WHITE = new("\e[37m") #: Color

LIGHT_BLACK = new("\e[90m") #: Color
LIGHT_RED = new("\e[91m") #: Color
LIGHT_GREEN = new("\e[92m") #: Color
LIGHT_YELLOW = new("\e[93m") #: Color
LIGHT_BLUE = new("\e[94m") #: Color
LIGHT_MAGENTA = new("\e[95m") #: Color
LIGHT_CYAN = new("\e[96m") #: Color
LIGHT_WHITE = new("\e[97m") #: Color
end

module Colorize
Expand Down
41 changes: 24 additions & 17 deletions lib/spoom/deadcode/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,33 @@ module Spoom
module Deadcode
# A definition is a class, module, method, constant, etc. being defined in the code
class Definition < T::Struct
class Kind < T::Enum
enums do
AttrReader = new("attr_reader")
AttrWriter = new("attr_writer")
Class = new("class")
Constant = new("constant")
Method = new("method")
Module = new("module")
class Kind
#: (String) -> void
def initialize(name)
@name = name
end
end

class Status < T::Enum
enums do
# A definition is marked as `ALIVE` if it has at least one reference with the same name
ALIVE = new
# A definition is marked as `DEAD` if it has no reference with the same name
DEAD = new
# A definition can be marked as `IGNORED` if it is not relevant for the analysis
IGNORED = new
# @override
#: -> String
def to_s
@name
end

AttrReader = new("attr_reader") #: Kind
AttrWriter = new("attr_writer") #: Kind
Class = new("class") #: Kind
Constant = new("constant") #: Kind
Method = new("method") #: Kind
Module = new("module") #: Kind
end

class Status
# A definition is marked as `ALIVE` if it has at least one reference with the same name
ALIVE = new #: Status
# A definition is marked as `DEAD` if it has no reference with the same name
DEAD = new #: Status
# A definition can be marked as `IGNORED` if it is not relevant for the analysis
IGNORED = new #: Status
end

const :kind, Kind
Expand Down
2 changes: 2 additions & 0 deletions lib/spoom/deadcode/remover.rb
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ def node_match_kind?(node, kind)
node.is_a?(Prism::DefNode)
when Definition::Kind::Module
node.is_a?(Prism::ModuleNode)
else
raise Error, "Unsupported node kind: #{node.class}"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/spoom/model/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def visit_call_node(node)
current_namespace.mixins << Extend.new(arg.slice)
end
when :public, :private, :protected
@visibility_stack << Visibility.from_serialized(node.name.to_s)
@visibility_stack << Visibility.from_string(node.name.to_s)
if node.arguments
super
@visibility_stack.pop
Expand Down
32 changes: 27 additions & 5 deletions lib/spoom/model/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,34 @@ class AttrReader < Attr; end
class AttrWriter < Attr; end
class AttrAccessor < Attr; end

class Visibility < T::Enum
enums do
Public = new("public")
Protected = new("protected")
Private = new("private")
class Visibility
class << self
#: (String) -> Visibility
def from_string(name)
case name
when "public" then Public
when "protected" then Protected
when "private" then Private
else
raise Error, "Invalid visibility: #{name}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to raise ArgumentError here:

Suggested change
raise Error, "Invalid visibility: #{name}"
raise ArgumentError, "Invalid visibility: #{name}"

end
end
end

#: (String) -> void
def initialize(name)
@name = name
end

# @override
#: -> String
def to_s
@name
end

Public = new("public") #: Visibility
Protected = new("protected") #: Visibility
Private = new("private") #: Visibility
end

# A mixin (include, prepend, extend) to a namespace
Expand Down
16 changes: 12 additions & 4 deletions lib/spoom/model/reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ class Model
# Constants could be classes, modules, or actual constants.
# Methods could be accessors, instance or class methods, aliases, etc.
class Reference < T::Struct
class Kind < T::Enum
enums do
Constant = new("constant")
Method = new("method")
class Kind
#: (String) -> void
def initialize(name)
@name = name
end

#: -> String
def to_s
@name
end

Constant = new("constant") #: Kind
Method = new("method") #: Kind
end

class << self
Expand Down
106 changes: 59 additions & 47 deletions rbi/spoom.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ module Spoom::Cli::Helper
def yellow(string); end
end

Spoom::Cli::Helper::HIGHLIGHT_COLOR = T.let(T.unsafe(nil), Spoom::Color)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be definitely be here: https://github.com/Shopify/spoom/blob/main/lib/spoom/cli/helper.rb#L106.

I think it wasn't before because the constant was pointing to the enum variant instead of a real constant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense!


class Spoom::Cli::Main < ::Thor
include ::Spoom::Colorize
include ::Spoom::Cli::Helper
Expand Down Expand Up @@ -212,32 +214,33 @@ Spoom::Cli::Srb::Tc::SORT_CODE = T.let(T.unsafe(nil), String)
Spoom::Cli::Srb::Tc::SORT_ENUM = T.let(T.unsafe(nil), Array)
Spoom::Cli::Srb::Tc::SORT_LOC = T.let(T.unsafe(nil), String)

class Spoom::Color < ::T::Enum
enums do
BLACK = new
BLUE = new
BOLD = new
CLEAR = new
CYAN = new
GREEN = new
LIGHT_BLACK = new
LIGHT_BLUE = new
LIGHT_CYAN = new
LIGHT_GREEN = new
LIGHT_MAGENTA = new
LIGHT_RED = new
LIGHT_WHITE = new
LIGHT_YELLOW = new
MAGENTA = new
RED = new
WHITE = new
YELLOW = new
end
class Spoom::Color
sig { params(ansi_code: ::String).void }
def initialize(ansi_code); end

sig { returns(::String) }
def ansi_code; end
end

Spoom::Color::BLACK = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::BLUE = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::BOLD = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::CLEAR = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::CYAN = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::GREEN = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_BLACK = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_BLUE = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_CYAN = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_GREEN = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_MAGENTA = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_RED = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_WHITE = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::LIGHT_YELLOW = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::MAGENTA = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::RED = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::WHITE = T.let(T.unsafe(nil), Spoom::Color)
Spoom::Color::YELLOW = T.let(T.unsafe(nil), Spoom::Color)

module Spoom::Colorize
sig { params(string: ::String, color: ::Spoom::Color).returns(::String) }
def set_color(string, *color); end
Expand Down Expand Up @@ -999,25 +1002,25 @@ class Spoom::Deadcode::Definition < ::T::Struct
def to_json(*args); end
end

class Spoom::Deadcode::Definition::Kind < ::T::Enum
enums do
AttrReader = new
AttrWriter = new
Class = new
Constant = new
Method = new
Module = new
end
end
class Spoom::Deadcode::Definition::Kind
sig { params(name: ::String).void }
def initialize(name); end

class Spoom::Deadcode::Definition::Status < ::T::Enum
enums do
ALIVE = new
DEAD = new
IGNORED = new
end
sig { override.returns(::String) }
def to_s; end
end

Spoom::Deadcode::Definition::Kind::AttrReader = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
Spoom::Deadcode::Definition::Kind::AttrWriter = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
Spoom::Deadcode::Definition::Kind::Class = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
Spoom::Deadcode::Definition::Kind::Constant = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
Spoom::Deadcode::Definition::Kind::Method = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
Spoom::Deadcode::Definition::Kind::Module = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Kind)
class Spoom::Deadcode::Definition::Status; end
Spoom::Deadcode::Definition::Status::ALIVE = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Status)
Spoom::Deadcode::Definition::Status::DEAD = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Status)
Spoom::Deadcode::Definition::Status::IGNORED = T.let(T.unsafe(nil), Spoom::Deadcode::Definition::Status)

class Spoom::Deadcode::ERB < ::Erubi::Engine
sig { params(input: T.untyped, properties: T.untyped).void }
def initialize(input, properties = T.unsafe(nil)); end
Expand Down Expand Up @@ -2277,13 +2280,14 @@ class Spoom::Model::Reference < ::T::Struct
end
end

class Spoom::Model::Reference::Kind < ::T::Enum
enums do
Constant = new
Method = new
end
class Spoom::Model::Reference::Kind
sig { params(name: ::String).void }
def initialize(name); end
end

Spoom::Model::Reference::Kind::Constant = T.let(T.unsafe(nil), Spoom::Model::Reference::Kind)
Spoom::Model::Reference::Kind::Method = T.let(T.unsafe(nil), Spoom::Model::Reference::Kind)

class Spoom::Model::ReferencesVisitor < ::Spoom::Visitor
sig { params(file: ::String).void }
def initialize(file); end
Expand Down Expand Up @@ -2433,14 +2437,22 @@ class Spoom::Model::UnresolvedSymbol < ::Spoom::Model::Symbol
def to_s; end
end

class Spoom::Model::Visibility < ::T::Enum
enums do
Private = new
Protected = new
Public = new
class Spoom::Model::Visibility
sig { params(name: ::String).void }
def initialize(name); end

sig { override.returns(::String) }
def to_s; end

class << self
sig { params(name: ::String).returns(::Spoom::Model::Visibility) }
def from_string(name); end
end
end

Spoom::Model::Visibility::Private = T.let(T.unsafe(nil), Spoom::Model::Visibility)
Spoom::Model::Visibility::Protected = T.let(T.unsafe(nil), Spoom::Model::Visibility)
Spoom::Model::Visibility::Public = T.let(T.unsafe(nil), Spoom::Model::Visibility)
class Spoom::ParseError < ::Spoom::Error; end

class Spoom::Poset
Expand Down
4 changes: 2 additions & 2 deletions test/spoom/model/builder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def m7; end
model.symbols.values
.flat_map(&:definitions)
.grep(Method)
.map { |d| "#{d.full_name}: #{T.cast(d, Method).visibility.serialize}" },
.map { |d| "#{d.full_name}: #{d.visibility}" },
)
end

Expand Down Expand Up @@ -390,7 +390,7 @@ def m6; end
model.symbols.values
.flat_map(&:definitions)
.grep(Method)
.map { |d| "#{d.full_name}: #{T.cast(d, Method).visibility.serialize}" },
.map { |d| "#{d.full_name}: #{d.visibility}" },
)
end

Expand Down
Loading