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

Remove redundant nil checks in RBI::Visitor #312

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions lib/rbi/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ module Indexable

interface!

requires_ancestor { Node }

# Unique IDs that refer to this node.
#
# Some nodes can have multiple ids, for example an attribute accessor matches the ID of the
Expand Down
1 change: 1 addition & 0 deletions lib/rbi/rewriters/annotate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def visit(node)
when Const, Attr, Method, TStructField, TypeMember
annotate_node(node) if @annotate_properties
end

visit_all(node.nodes) if node.is_a?(Tree)
end

Expand Down
1 change: 1 addition & 0 deletions lib/rbi/rewriters/deannotate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def visit(node)
when Scope, Const, Attr, Method, TStructField, TypeMember
deannotate_node(node)
end

visit_all(node.nodes) if node.is_a?(Tree)
end

Expand Down
25 changes: 11 additions & 14 deletions lib/rbi/rewriters/group_nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,21 @@ class GroupNodes < Visitor

sig { override.params(node: T.nilable(Node)).void }
def visit(node)
return unless node
return unless node.is_a?(Tree)

case node
when Tree
kinds = node.nodes.map { |child| group_kind(child) }
kinds.uniq!

groups = {}
kinds.each { |kind| groups[kind] = Group.new(kind) }
kinds = node.nodes.map { |child| group_kind(child) }
kinds.uniq!

node.nodes.dup.each do |child|
visit(child)
child.detach
groups[group_kind(child)] << child
end
groups = {}
kinds.each { |kind| groups[kind] = Group.new(kind) }

groups.each { |_, group| node << group }
node.nodes.dup.each do |child|
visit(child)
child.detach
groups[group_kind(child)] << child
end

groups.each { |_, group| node << group }
end

private
Expand Down
2 changes: 0 additions & 2 deletions lib/rbi/rewriters/merge_trees.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,6 @@ def initialize(output, left_name: "left", right_name: "right", keep: Keep::NONE)

sig { override.params(node: T.nilable(Node)).void }
def visit(node)
return unless node
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing this causes a type failure before, even if I make Indexable a subtype of Node (by adding requires_ancestor { Node }):

lib/rbi/rewriters/merge_trees.rb:155: Expected RBI::Node but found T.all(RBI::Indexable, T.nilable(RBI::Node)) for argument right https://srb.help/7002
     155 |                make_conflict_tree(prev, node)
                                                   ^^^^

Should T.all(RBI::Indexable, T.nilable(RBI::Node)) be simplified to T.all(RBI::Indexable, RBI::Node) (since it needs to be Indexable, it can't also be nil), and therefore be a subtype of RBI::Node?


case node
when Scope
prev = previous_definition(node)
Expand Down
47 changes: 22 additions & 25 deletions lib/rbi/rewriters/nest_non_public_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,30 @@ class NestNonPublicMethods < Visitor

sig { override.params(node: T.nilable(Node)).void }
def visit(node)
return unless node

case node
when Tree
Comment on lines -13 to -14
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing these case statements with only one when worked out nicely.

public_group = VisibilityGroup.new(Public.new)
protected_group = VisibilityGroup.new(Protected.new)
private_group = VisibilityGroup.new(Private.new)

node.nodes.dup.each do |child|
visit(child)
next unless child.is_a?(Method)

child.detach
case child.visibility
when Protected
protected_group << child
when Private
private_group << child
else
public_group << child
end
return unless node.is_a?(Tree)

public_group = VisibilityGroup.new(Public.new)
protected_group = VisibilityGroup.new(Protected.new)
private_group = VisibilityGroup.new(Private.new)

node.nodes.dup.each do |child|
visit(child)
next unless child.is_a?(Method)

child.detach
case child.visibility
when Protected
protected_group << child
when Private
private_group << child
else
public_group << child
end

node << public_group unless public_group.empty?
node << protected_group unless protected_group.empty?
node << private_group unless private_group.empty?
end

node << public_group unless public_group.empty?
node << protected_group unless protected_group.empty?
node << private_group unless private_group.empty?
end
end
end
Expand Down
23 changes: 10 additions & 13 deletions lib/rbi/rewriters/nest_singleton_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@ class NestSingletonMethods < Visitor

sig { override.params(node: T.nilable(Node)).void }
def visit(node)
return unless node
return unless node.is_a?(Tree)

case node
when Tree
singleton_class = SingletonClass.new
singleton_class = SingletonClass.new

node.nodes.dup.each do |child|
visit(child)
next unless child.is_a?(Method) && child.is_singleton
node.nodes.dup.each do |child|
visit(child)
next unless child.is_a?(Method) && child.is_singleton

child.detach
child.is_singleton = false
singleton_class << child
end

node << singleton_class unless singleton_class.empty?
child.detach
child.is_singleton = false
singleton_class << child
end

node << singleton_class unless singleton_class.empty?
end
end
end
Expand Down
2 changes: 0 additions & 2 deletions lib/rbi/rewriters/remove_known_definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ def visit_all(nodes)

sig { override.params(node: T.nilable(Node)).void }
def visit(node)
return unless node
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not convinced removing these is a great idea. They're technically redundant, but they also fail fast on nil, which would be a really common case to hit (rather than doing 3 Class#=== checks below)


case node
when Scope
visit_all(node.nodes)
Expand Down
28 changes: 14 additions & 14 deletions lib/rbi/rewriters/sort_nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ def visit(node)
return unless node.is_a?(Tree)

visit_all(node.nodes)
original_order = node.nodes.map.with_index.to_h
original_order = node.nodes.each_with_index.to_h

# The child nodes could contain private/protected markers. If so, they should not be moved in the file.
# Otherwise, some methods could see their privacy change. To avoid that problem, divide the array of child
# nodes into chunks based on whether any Visibility nodes appear, and sort the chunks independently. This
# applies the ordering rules from the node_rank method as much as possible, while preserving visibility.
sorted_nodes = node.nodes.chunk do |n|
n.is_a?(Visibility)
end.flat_map do |_, nodes|
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no Rubocop rule that can enforce this (last I checked), but having end.something_else do || on the same line is kinda weird, and reads better when it's one operation per line IMO.

nodes.sort! do |a, b|
# First we try to compare the nodes by their node rank (based on the node type)
res = node_rank(a) <=> node_rank(b)
next res if res != 0 # we can sort the nodes by their rank, let's stop here
sorted_nodes = node.nodes
.chunk { |n| n.is_a?(Visibility) }
.flat_map do |_, nodes|
nodes.sort! do |a, b|
# First we try to compare the nodes by their node rank (based on the node type)
res = node_rank(a) <=> node_rank(b)
next res if res != 0 # we can sort the nodes by their rank, let's stop here

# Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name
res = node_name(a) <=> node_name(b)
next res if res && res != 0 # we can sort the nodes by their name, let's stop here
# Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name
res = node_name(a) <=> node_name(b)
next res if res && res != 0 # we can sort the nodes by their name, let's stop here

# Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then,
T.must(original_order[a]) <=> T.must(original_order[b]) # we keep the original order
# Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then,
T.must(original_order[a]) <=> T.must(original_order[b]) # we keep the original order
end
end
end

node.nodes.replace(sorted_nodes)
end
Expand Down
Loading