Skip to content

Commit

Permalink
Add sorbet type checking to bundler/file_updater/gemspec_sanitizer.rb (
Browse files Browse the repository at this point in the history
…#11401)

* Add sorbet type checking to bundler/file_updater/gemspec_sanitizer.rb

* Update gemspec_sanitizer.rb

* Add sorbet type checking to bundler/file_updater/gemspec_sanitizer.rb

* Replace T.untyped
  • Loading branch information
amazimbe authored Jan 27, 2025
1 parent b64d6a4 commit 97573d2
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# typed: strict
# typed: strong
# frozen_string_literal: true

require "sorbet-runtime"
Expand Down
51 changes: 47 additions & 4 deletions bundler/lib/dependabot/bundler/file_updater/gemspec_sanitizer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require "parser/current"
Expand All @@ -8,6 +8,8 @@ module Dependabot
module Bundler
class FileUpdater
class GemspecSanitizer
extend T::Sig

UNNECESSARY_ASSIGNMENTS = %i(
bindir=
cert_chain=
Expand All @@ -23,12 +25,15 @@ class GemspecSanitizer
rdoc_options=
).freeze

sig { returns(String) }
attr_reader :replacement_version

sig { params(replacement_version: T.any(String, Integer, Gem::Version)).void }
def initialize(replacement_version:)
@replacement_version = replacement_version
@replacement_version = T.let(replacement_version.to_s, String)
end

sig { params(content: String).returns(String) }
def rewrite(content)
buffer = Parser::Source::Buffer.new("(gemspec_content)")
buffer.source = content
Expand All @@ -47,10 +52,16 @@ def rewrite(content)
end

class Rewriter < Parser::TreeRewriter
extend T::Sig

ParserNode = T.type_alias { T.nilable(T.any(Parser::AST::Node, Symbol, Integer, String, Float)) }

sig { params(replacement_version: String).void }
def initialize(replacement_version:)
@replacement_version = replacement_version
end

sig { params(node: Parser::AST::Node).void }
def on_send(node)
# Wrap any `require` or `require_relative` calls in a rescue
# block, as we might not have the required files
Expand Down Expand Up @@ -82,12 +93,15 @@ def on_send(node)

private

sig { returns(String) }
attr_reader :replacement_version

sig { params(node: Parser::AST::Node).returns(T::Boolean) }
def requires_file?(node)
%i(require require_relative).include?(node.children[1])
end

sig { params(node: Parser::AST::Node).void }
def wrap_require(node)
replace(
node.loc.expression,
Expand All @@ -98,6 +112,7 @@ def wrap_require(node)
)
end

sig { params(node: ParserNode).void }
def replace_version_assignments(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -106,6 +121,7 @@ def replace_version_assignments(node)
node.children.each { |child| replace_version_assignments(child) }
end

sig { params(node: T.nilable(Parser::AST::Node)).void }
def replace_version_constant_references(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -116,6 +132,7 @@ def replace_version_constant_references(node)
end
end

sig { params(node: ParserNode).void }
def replace_file_assignments(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -124,6 +141,7 @@ def replace_file_assignments(node)
node.children.each { |child| replace_file_assignments(child) }
end

sig { params(node: ParserNode).void }
def replace_require_paths_assignments(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -134,6 +152,7 @@ def replace_require_paths_assignments(node)
end
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_assigns_to_version_constant?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -146,6 +165,7 @@ def node_assigns_to_version_constant?(node)
node_interpolates_version_constant?(node.children.last)
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_assigns_files_to_var?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -155,6 +175,7 @@ def node_assigns_files_to_var?(node)
node_dynamically_lists_files?(node.children[2])
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_dynamically_lists_files?(node)
return false unless node.is_a?(Parser::AST::Node)

Expand All @@ -163,6 +184,7 @@ def node_dynamically_lists_files?(node)
node.type == :block && node.children.first&.type == :send
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_assigns_require_paths?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -171,6 +193,7 @@ def node_assigns_require_paths?(node)
node.children[1] == :require_paths=
end

sig { params(node: T.nilable(T.any(Parser::AST::Node, Symbol, String))).void }
def replace_file_reads(node)
return unless node.is_a?(Parser::AST::Node)
return if node.children[1] == :version=
Expand All @@ -180,6 +203,7 @@ def replace_file_reads(node)
node.children.each { |child| replace_file_reads(child) }
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_reads_a_file?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -189,6 +213,7 @@ def node_reads_a_file?(node)
node.children[1] == :read
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_uses_readlines?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -198,6 +223,7 @@ def node_uses_readlines?(node)
node.children[1] == :readlines
end

sig { params(node: T.nilable(T.any(Parser::AST::Node, Symbol, String))).void }
def replace_json_parses(node)
return unless node.is_a?(Parser::AST::Node)
return if node.children[1] == :version=
Expand All @@ -206,6 +232,7 @@ def replace_json_parses(node)
node.children.each { |child| replace_json_parses(child) }
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_parses_json?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -215,6 +242,7 @@ def node_parses_json?(node)
node.children[1] == :parse
end

sig { params(node: T.nilable(T.any(Parser::AST::Node, Symbol, String))).void }
def remove_find_dot_find_args(node)
return unless node.is_a?(Parser::AST::Node)
return if node.children[1] == :version=
Expand All @@ -223,6 +251,7 @@ def remove_find_dot_find_args(node)
node.children.each { |child| remove_find_dot_find_args(child) }
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_calls_find_dot_find?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)
Expand All @@ -232,6 +261,7 @@ def node_calls_find_dot_find?(node)
node.children[1] == :find
end

sig { params(node: ParserNode).void }
def remove_unnecessary_assignments(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -247,15 +277,17 @@ def remove_unnecessary_assignments(node)
end
end

sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Boolean) }
def node_includes_heredoc?(node)
find_heredoc_end_range(node)
!!find_heredoc_end_range(node)
end

# Performs a depth-first search for the first heredoc in the given
# Parser::AST::Node.
#
# Returns a Parser::Source::Range identifying the location of the end
# of the heredoc, or nil if no heredoc was found.
sig { params(node: T.nilable(Parser::AST::Node)).returns(T.nilable(Parser::Source::Range)) }
def find_heredoc_end_range(node)
return unless node.is_a?(Parser::AST::Node)

Expand All @@ -271,30 +303,34 @@ def find_heredoc_end_range(node)
nil
end

sig { params(node: ParserNode).returns(T::Boolean) }
def unnecessary_assignment?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.children.first.is_a?(Parser::AST::Node)

return true if node.children.first.type == :lvar &&
UNNECESSARY_ASSIGNMENTS.include?(node.children[1])

node.children[1] == :[]= && node.children.first.children.last
!!(node.children[1] == :[]= && node.children.first.children.last)
end

sig { params(node: ParserNode).returns(T::Boolean) }
def node_is_version_constant?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.type == :const

node.children.last.to_s.match?(/version/i)
end

sig { params(node: ParserNode).returns(T::Boolean) }
def node_calls_version_constant?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.type == :send

node.children.any? { |n| node_is_version_constant?(n) }
end

sig { params(node: ParserNode).returns(T::Boolean) }
def node_interpolates_version_constant?(node)
return false unless node.is_a?(Parser::AST::Node)
return false unless node.type == :dstr
Expand All @@ -305,6 +341,7 @@ def node_interpolates_version_constant?(node)
.any? { |n| node_is_version_constant?(n) }
end

sig { params(node: Parser::AST::Node).void }
def replace_constant(node)
case node.children.last&.type
when :str, :int then nil # no-op
Expand All @@ -318,29 +355,35 @@ def replace_constant(node)
end
end

sig { params(node: Parser::AST::Node).void }
def replace_file_assignment(node)
replace(node.children.last.loc.expression, "[]")
end

sig { params(node: Parser::AST::Node).void }
def replace_require_paths_assignment(node)
replace(node.children.last.loc.expression, "['lib']")
end

sig { params(node: Parser::AST::Node).void }
def replace_file_read(node)
replace(node.loc.expression, %("#{replacement_version}"))
end

sig { params(node: Parser::AST::Node).void }
def replace_json_parse(node)
replace(
node.loc.expression,
%({ "version" => "#{replacement_version}" })
)
end

sig { params(node: Parser::AST::Node).void }
def replace_file_readlines(node)
replace(node.loc.expression, %(["#{replacement_version}"]))
end

sig { params(node: Parser::AST::Node).void }
def remove_find_args(node)
last_arg = node.children.last

Expand Down

0 comments on commit 97573d2

Please sign in to comment.