mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Merge pull request #301 from crystal-ameba/Sija/style-redundant-parentheses-rule
Add `Style/RedundantParentheses` rule
This commit is contained in:
commit
28e2871165
3 changed files with 172 additions and 1 deletions
101
spec/ameba/rule/style/redundant_parentheses_spec.cr
Normal file
101
spec/ameba/rule/style/redundant_parentheses_spec.cr
Normal file
|
@ -0,0 +1,101 @@
|
|||
require "../../../spec_helper"
|
||||
|
||||
module Ameba::Rule::Style
|
||||
subject = RedundantParentheses.new
|
||||
|
||||
describe RedundantParentheses do
|
||||
{% for keyword in %w(if unless while until) %}
|
||||
context "{{ keyword.id }}" do
|
||||
it "reports if redundant parentheses are found" do
|
||||
source = expect_issue subject, <<-CRYSTAL, keyword: {{ keyword }}
|
||||
%{keyword} (foo > 10)
|
||||
_{keyword} # ^^^^^^^^^^ error: Redundant parentheses
|
||||
foo
|
||||
end
|
||||
CRYSTAL
|
||||
|
||||
expect_correction source, <<-CRYSTAL
|
||||
{{ keyword.id }} foo > 10
|
||||
foo
|
||||
end
|
||||
CRYSTAL
|
||||
end
|
||||
end
|
||||
{% end %}
|
||||
|
||||
context "case" do
|
||||
it "reports if redundant parentheses are found" do
|
||||
source = expect_issue subject, <<-CRYSTAL
|
||||
case (foo = @foo)
|
||||
# ^^^^^^^^^^^^ error: Redundant parentheses
|
||||
when String then "string"
|
||||
when Symbol then "symbol"
|
||||
end
|
||||
CRYSTAL
|
||||
|
||||
expect_correction source, <<-CRYSTAL
|
||||
case foo = @foo
|
||||
when String then "string"
|
||||
when Symbol then "symbol"
|
||||
end
|
||||
CRYSTAL
|
||||
end
|
||||
end
|
||||
|
||||
context "properties" do
|
||||
context "#exclude_ternary=" do
|
||||
it "skips ternary control expressions by default" do
|
||||
expect_no_issues subject, <<-CRYSTAL
|
||||
(foo > bar) ? true : false
|
||||
CRYSTAL
|
||||
end
|
||||
|
||||
it "allows to configure assignments" do
|
||||
rule = Rule::Style::RedundantParentheses.new
|
||||
rule.exclude_ternary = false
|
||||
|
||||
expect_issue rule, <<-CRYSTAL
|
||||
(foo.empty?) ? true : false
|
||||
# ^^^^^^^^^^ error: Redundant parentheses
|
||||
CRYSTAL
|
||||
|
||||
expect_no_issues subject, <<-CRYSTAL
|
||||
(foo && bar) ? true : false
|
||||
(foo || bar) ? true : false
|
||||
(foo = @foo) ? true : false
|
||||
foo == 42 ? true : false
|
||||
(foo = 42) ? true : false
|
||||
(foo > 42) ? true : false
|
||||
(foo >= 42) ? true : false
|
||||
(3 >= foo >= 42) ? true : false
|
||||
(3.in? 0..42) ? true : false
|
||||
(yield 42) ? true : false
|
||||
(foo rescue 42) ? true : false
|
||||
CRYSTAL
|
||||
end
|
||||
end
|
||||
|
||||
context "#exclude_assignments=" do
|
||||
it "reports assignments by default" do
|
||||
expect_issue subject, <<-CRYSTAL
|
||||
if (foo = @foo)
|
||||
# ^^^^^^^^^^^^ error: Redundant parentheses
|
||||
foo
|
||||
end
|
||||
CRYSTAL
|
||||
end
|
||||
|
||||
it "allows to configure assignments" do
|
||||
rule = Rule::Style::RedundantParentheses.new
|
||||
rule.exclude_assignments = true
|
||||
|
||||
expect_no_issues rule, <<-CRYSTAL
|
||||
if (foo = @foo)
|
||||
foo
|
||||
end
|
||||
CRYSTAL
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
70
src/ameba/rule/style/redundant_parentheses.cr
Normal file
70
src/ameba/rule/style/redundant_parentheses.cr
Normal file
|
@ -0,0 +1,70 @@
|
|||
module Ameba::Rule::Style
|
||||
# A rule that disallows redundant parentheses around control expressions.
|
||||
#
|
||||
# For example, this is considered invalid:
|
||||
#
|
||||
# ```
|
||||
# if (foo == 42)
|
||||
# do_something
|
||||
# end
|
||||
# ```
|
||||
#
|
||||
# And should be replaced by the following:
|
||||
#
|
||||
# ```
|
||||
# if foo == 42
|
||||
# do_something
|
||||
# end
|
||||
# ```
|
||||
#
|
||||
# YAML configuration example:
|
||||
#
|
||||
# ```
|
||||
# Style/RedundantParentheses:
|
||||
# Enabled: true
|
||||
# ExcludeTernary: false
|
||||
# ExcludeAssignments: false
|
||||
# ```
|
||||
class RedundantParentheses < Base
|
||||
properties do
|
||||
description "Disallows redundant parentheses around control expressions"
|
||||
|
||||
exclude_ternary false
|
||||
exclude_assignments false
|
||||
end
|
||||
|
||||
MSG = "Redundant parentheses"
|
||||
|
||||
protected def strip_parentheses?(node, in_ternary) : Bool
|
||||
case node
|
||||
when Crystal::BinaryOp, Crystal::ExceptionHandler
|
||||
!in_ternary
|
||||
when Crystal::Call
|
||||
!in_ternary || node.has_parentheses? || node.args.empty?
|
||||
when Crystal::Yield
|
||||
!in_ternary || node.has_parentheses? || node.exps.empty?
|
||||
when Crystal::Assign, Crystal::OpAssign, Crystal::MultiAssign
|
||||
!in_ternary && !exclude_assignments
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case | Crystal::While | Crystal::Until)
|
||||
is_ternary = node.is_a?(Crystal::If) && node.ternary?
|
||||
|
||||
return if is_ternary && exclude_ternary
|
||||
|
||||
return unless (cond = node.cond).is_a?(Crystal::Expressions)
|
||||
return unless cond.keyword.paren?
|
||||
|
||||
return unless exp = cond.single_expression?
|
||||
return unless strip_parentheses?(exp, is_ternary)
|
||||
|
||||
issue_for cond, MSG do |corrector|
|
||||
corrector.remove_trailing(cond, 1)
|
||||
corrector.remove_leading(cond, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -73,7 +73,7 @@ class Ameba::Spec::AnnotatedSource
|
|||
private def message_to_regex(expected_annotation)
|
||||
String.build do |io|
|
||||
offset = 0
|
||||
while (index = expected_annotation.index(ABBREV, offset))
|
||||
while index = expected_annotation.index(ABBREV, offset)
|
||||
io << Regex.escape(expected_annotation[offset...index])
|
||||
io << ".*?"
|
||||
offset = index + ABBREV.size
|
||||
|
|
Loading…
Reference in a new issue