Skip to content

Commit

Permalink
Make custom cop inherit RuboCop::Cop::Base
Browse files Browse the repository at this point in the history
Follow up Shopify#240 (comment).

The legacy `Cop::Cop` API is deprecated and this PR use new `Cop::Base` API instead.

> maintain any RuboCop extensions, as the legacy API will be removed in RuboCop 2.0.

https://metaredux.com/posts/2020/10/21/rubocop-1-0.html
  • Loading branch information
koic committed Jul 11, 2024
1 parent 4ab4032 commit 4222c34
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,21 @@ module Sorbet
# # rbi/external_interface.rbi
# # sorbet/rbi/some_file.rbi
# # sorbet/rbi/any/path/for/file.rbi
class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Base
include RangeHelp

def investigate(processed_source)
def on_new_investigation
paths = allowed_paths

if paths.nil?
add_offense(
nil,
location: source_range(processed_source.buffer, 1, 0),
source_range(processed_source.buffer, 1, 0),
message: "AllowedPaths expects an array",
)
return
elsif paths.empty?
add_offense(
nil,
location: source_range(processed_source.buffer, 1, 0),
source_range(processed_source.buffer, 1, 0),
message: "AllowedPaths cannot be empty",
)
return
Expand All @@ -47,8 +45,7 @@ def investigate(processed_source)
rel_path = processed_source.file_path.sub("#{Dir.pwd}/", "")

add_offense(
nil,
location: source_range(processed_source.buffer, 1, 0),
source_range(processed_source.buffer, 1, 0),
message: "RBI file path should match one of: #{paths.join(", ")}",
) if paths.none? { |pattern| File.fnmatch(pattern, rel_path) }
end
Expand Down
55 changes: 27 additions & 28 deletions lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module Sorbet
class EnforceSigilOrder < ValidSigil
include RangeHelp

def investigate(processed_source)
def on_new_investigation
return if processed_source.tokens.empty?

tokens = extract_magic_comments(processed_source)
Expand All @@ -41,30 +41,6 @@ def investigate(processed_source)
check_magic_comments_order(tokens)
end

def autocorrect(_node)
lambda do |corrector|
tokens = extract_magic_comments(processed_source)

# Get the magic comments tokens in their expected order
expected = PREFERRED_ORDER.keys.map do |re|
tokens.select { |token| re.match?(token.text) }
end.flatten

tokens.each_with_index do |token, index|
corrector.replace(token.pos, expected[index].text)
end

# Remove blank lines between the magic comments
lines = tokens.map(&:line).to_set
(lines.min...lines.max).each do |line|
next if lines.include?(line)
next unless processed_source[line - 1].empty?

corrector.remove(source_range(processed_source.buffer, line, 0))
end
end
end

protected

CODING_REGEX = /#\s+(en)?coding:(?:\s+([\w]+))?/
Expand Down Expand Up @@ -105,13 +81,36 @@ def check_magic_comments_order(tokens)
if order != expected
tokens.each do |token|
add_offense(
token,
location: token.pos,
token.pos,
message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(", ")}.",
)
) do |corrector|
autocorrect(corrector)
end
end
end
end

def autocorrect(corrector)
tokens = extract_magic_comments(processed_source)

# Get the magic comments tokens in their expected order
expected = PREFERRED_ORDER.keys.map do |re|
tokens.select { |token| re.match?(token.text) }
end.flatten

tokens.each_with_index do |token, index|
corrector.replace(token.pos, expected[index].text)
end

# Remove blank lines between the magic comments
lines = tokens.map(&:line).to_set
(lines.min...lines.max).each do |line|
next if lines.include?(line)
next unless processed_source[line - 1].empty?

corrector.remove(source_range(processed_source.buffer, line, 0))
end
end
end
end
end
Expand Down
30 changes: 15 additions & 15 deletions lib/rubocop/cop/sorbet/sigils/enforce_single_sigil.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,15 @@ module Sorbet
class EnforceSingleSigil < ValidSigil
include RangeHelp

def investigate(processed_source)
def on_new_investigation
return if processed_source.tokens.empty?

sigils = extract_all_sigils(processed_source)
return if sigils.empty?

sigils[1..sigils.size].each do |token|
add_offense(token, location: token.pos, message: "Files must only contain one sigil")
end
end

def autocorrect(_node)
->(corrector) do
sigils = extract_all_sigils(processed_source)
return if sigils.empty?

# The first sigil encountered represents the "real" strictness so remove any below
sigils[1..sigils.size].each do |token|
corrector.remove(
source_range(processed_source.buffer, token.line, (0..token.pos.last_column)),
)
add_offense(token.pos, message: "Files must only contain one sigil") do |corrector|
autocorrect(corrector)
end
end
end
Expand All @@ -58,6 +46,18 @@ def extract_all_sigils(processed_source)
.take_while { |token| token.type == :tCOMMENT }
.find_all { |token| SIGIL_REGEX.match?(token.text) }
end

def autocorrect(corrector)
sigils = extract_all_sigils(processed_source)
return if sigils.empty?

# The first sigil encountered represents the "real" strictness so remove any below
sigils[1..sigils.size].each do |token|
corrector.remove(
source_range(processed_source.buffer, token.line, (0..token.pos.last_column)),
)
end
end
end
end
end
Expand Down
71 changes: 38 additions & 33 deletions lib/rubocop/cop/sorbet/sigils/valid_sigil.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ module Sorbet
#
# If an `ExactStrictness` level is specified, it will be used in offense messages and autocorrect.
# Otherwise, if a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
class ValidSigil < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
class ValidSigil < RuboCop::Cop::Base
extend AutoCorrector

@registry = Registry.global # So we can properly subclass this cop

def investigate(processed_source)
def on_new_investigation
return if processed_source.tokens.empty?

sigil = extract_sigil(processed_source)
Expand All @@ -33,22 +35,6 @@ def investigate(processed_source)
nil unless check_strictness_level(sigil, strictness)
end

def autocorrect(_node)
lambda do |corrector|
return unless require_sigil_on_all_files?
return unless extract_sigil(processed_source).nil?

token = processed_source.tokens.first
replace_with = suggested_strictness_level
sigil = "# typed: #{replace_with}"
if token.text.start_with?("#!") # shebang line
corrector.insert_after(token.pos, "\n#{sigil}")
else
corrector.insert_before(token.pos, "#{sigil}\n")
end
end
end

protected

STRICTNESS_LEVELS = ["ignore", "false", "true", "strict", "strong"]
Expand All @@ -75,11 +61,12 @@ def check_sigil_present(sigil)
if require_sigil_on_all_files?
strictness = suggested_strictness_level
add_offense(
token,
location: token.pos,
token.pos,
message: "No Sorbet sigil found in file. " \
"Try a `typed: #{strictness}` to start (you can also use `rubocop -a` to automatically add this).",
)
) do |corrector|
autocorrect(corrector)
end
end
false
end
Expand Down Expand Up @@ -110,21 +97,23 @@ def check_strictness_not_empty(sigil, strictness)
return true if strictness

add_offense(
sigil,
location: sigil.pos,
sigil.pos,
message: "Sorbet sigil should not be empty.",
)
) do |corrector|
autocorrect(corrector)
end
false
end

def check_strictness_valid(sigil, strictness)
return true if STRICTNESS_LEVELS.include?(strictness)

add_offense(
sigil,
location: sigil.pos,
sigil.pos,
message: "Invalid Sorbet sigil `#{strictness}`.",
)
) do |corrector|
autocorrect(corrector)
end
false
end

Expand All @@ -137,27 +126,43 @@ def check_strictness_level(sigil, strictness)
exact_level = STRICTNESS_LEVELS.index(exact_strictness)
if current_level != exact_level
add_offense(
sigil,
location: sigil.pos,
sigil.pos,
message: "Sorbet sigil should be `#{exact_strictness}` got `#{strictness}`.",
)
) do |corrector|
autocorrect(corrector)
end
return false
end
else
minimum_level = STRICTNESS_LEVELS.index(minimum_strictness)
if current_level < minimum_level
add_offense(
sigil,
location: sigil.pos,
sigil.pos,
message: "Sorbet sigil should be at least `#{minimum_strictness}` got `#{strictness}`.",
)
) do |corrector|
autocorrect(corrector)
end
return false
end
end

true
end

def autocorrect(corrector)
return unless require_sigil_on_all_files?
return unless extract_sigil(processed_source).nil?

token = processed_source.tokens.first
replace_with = suggested_strictness_level
sigil = "# typed: #{replace_with}"
if token.text.start_with?("#!") # shebang line
corrector.insert_after(token.pos, "\n#{sigil}")
else
corrector.insert_before(token.pos, "#{sigil}\n")
end
end

# options

# Default is `false`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module Sorbet
#
# # good
# sig { void }
class CheckedTrueInSignature < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
class CheckedTrueInSignature < ::RuboCop::Cop::Base
include(RuboCop::Cop::RangeHelp)
include(RuboCop::Cop::Sorbet::SignatureHelp)

Expand All @@ -37,8 +37,7 @@ def on_signature(node)
return unless error

add_offense(
error,
location: source_range(
source_range(
processed_source.buffer,
error.location.line,
(error.location.selector.begin_pos)..(error.location.end.begin_pos),
Expand Down
43 changes: 22 additions & 21 deletions lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ module Sorbet
#
# * `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped')
# * `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped')
class EnforceSignatures < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
class EnforceSignatures < ::RuboCop::Cop::Base
extend AutoCorrector
include SignatureHelp

def initialize(config = nil, options = nil)
Expand Down Expand Up @@ -53,25 +54,6 @@ def on_signature(node)
@last_sig_for_scope[scope(node)] = node
end

def autocorrect(node)
lambda do |corrector|
suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)

if node.is_a?(RuboCop::AST::DefNode) # def something
node.arguments.each do |arg|
suggest.params << arg.children.first
end
elsif accessor?(node) # attr reader, writer, accessor
method = node.children[1]
symbol = node.children[2]
suggest.params << symbol.value if symbol && (method == :attr_writer || method == :attr_accessor)
suggest.returns = "void" if method == :attr_writer
end

corrector.insert_before(node, suggest.to_autocorrect)
end
end

def scope(node)
return unless node.parent
return node.parent if [:begin, :block, :class, :module].include?(node.parent.type)
Expand All @@ -87,11 +69,30 @@ def check_node(node)
add_offense(
node,
message: "Each method is required to have a signature.",
)
) do |corrector|
autocorrect(corrector, node)
end
end
@last_sig_for_scope[scope] = nil
end

def autocorrect(corrector, node)
suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)

if node.is_a?(RuboCop::AST::DefNode) # def something
node.arguments.each do |arg|
suggest.params << arg.children.first
end
elsif accessor?(node) # attr reader, writer, accessor
method = node.children[1]
symbol = node.children[2]
suggest.params << symbol.value if symbol && (method == :attr_writer || method == :attr_accessor)
suggest.returns = "void" if method == :attr_writer
end

corrector.insert_before(node, suggest.to_autocorrect)
end

def param_type_placeholder
cop_config["ParameterTypePlaceholder"] || "T.untyped"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module Sorbet
# # good
# sig { params(b: String, a: Integer).void }
# def foo(b:, a: 1); end
class KeywordArgumentOrdering < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
class KeywordArgumentOrdering < ::RuboCop::Cop::Base
include SignatureHelp

def on_signature(node)
Expand Down
Loading

0 comments on commit 4222c34

Please sign in to comment.