mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Add autocorrect for Style/UnlessElse
This commit is contained in:
parent
e6ebca7a5b
commit
d7795c0d7d
5 changed files with 122 additions and 2 deletions
|
@ -13,7 +13,7 @@ module Ameba::Rule::Style
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails if unless has else" do
|
it "fails if unless has else" do
|
||||||
expect_issue subject, <<-CRYSTAL
|
source = expect_issue subject, <<-CRYSTAL
|
||||||
unless something
|
unless something
|
||||||
# ^^^^^^^^^^^^^^ error: Favour if over unless with else
|
# ^^^^^^^^^^^^^^ error: Favour if over unless with else
|
||||||
:one
|
:one
|
||||||
|
@ -21,6 +21,14 @@ module Ameba::Rule::Style
|
||||||
:two
|
:two
|
||||||
end
|
end
|
||||||
CRYSTAL
|
CRYSTAL
|
||||||
|
|
||||||
|
expect_correction source, <<-CRYSTAL
|
||||||
|
if something
|
||||||
|
:two
|
||||||
|
else
|
||||||
|
:one
|
||||||
|
end
|
||||||
|
CRYSTAL
|
||||||
end
|
end
|
||||||
|
|
||||||
it "reports rule, pos and message" do
|
it "reports rule, pos and message" do
|
||||||
|
|
|
@ -46,5 +46,22 @@ module Ameba
|
||||||
s.matches_path?("new_source.cr").should be_false
|
s.matches_path?("new_source.cr").should be_false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#pos" do
|
||||||
|
it "works" do
|
||||||
|
s = Source.new <<-EOS
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
fizz
|
||||||
|
buzz
|
||||||
|
EOS
|
||||||
|
loc = Crystal::Location.new("", 2, 1)
|
||||||
|
end_loc = Crystal::Location.new("", 3, 4)
|
||||||
|
s.code[s.pos(loc)...s.pos(end_loc, end: true)].should eq <<-EOS
|
||||||
|
bar
|
||||||
|
fizz
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,7 +50,30 @@ module Ameba::Rule::Style
|
||||||
MSG = "Favour if over unless with else"
|
MSG = "Favour if over unless with else"
|
||||||
|
|
||||||
def test(source, node : Crystal::Unless)
|
def test(source, node : Crystal::Unless)
|
||||||
issue_for node, MSG unless node.else.nop?
|
return if node.else.nop?
|
||||||
|
return unless location = node.location
|
||||||
|
return unless cond_end_location = node.cond.end_location
|
||||||
|
return unless else_location = node.else_location
|
||||||
|
return unless end_location = node.end_location
|
||||||
|
|
||||||
|
issue_for node, MSG do |corrector|
|
||||||
|
keyword_begin_pos = source.pos(location)
|
||||||
|
keyword_end_pos = keyword_begin_pos + {{ "unless".size }}
|
||||||
|
keyword_range = keyword_begin_pos...keyword_end_pos
|
||||||
|
|
||||||
|
cond_end_pos = source.pos(cond_end_location, end: true)
|
||||||
|
else_begin_pos = source.pos(else_location)
|
||||||
|
body_range = cond_end_pos...else_begin_pos
|
||||||
|
|
||||||
|
else_end_pos = else_begin_pos + {{ "else".size }}
|
||||||
|
end_end_pos = source.pos(end_location, end: true)
|
||||||
|
end_begin_pos = end_end_pos - {{ "end".size }}
|
||||||
|
else_range = else_end_pos...end_begin_pos
|
||||||
|
|
||||||
|
corrector.replace(keyword_range, "if")
|
||||||
|
corrector.replace(body_range, source.code[else_range])
|
||||||
|
corrector.replace(else_range, source.code[body_range])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,5 +75,13 @@ module Ameba
|
||||||
def matches_path?(filepath)
|
def matches_path?(filepath)
|
||||||
path.in?(filepath, File.expand_path(filepath))
|
path.in?(filepath, File.expand_path(filepath))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Converts an AST location to a string position.
|
||||||
|
def pos(location : Crystal::Location, end end_pos = false) : Int32
|
||||||
|
line, column = location.line_number, location.column_number
|
||||||
|
pos = lines[0...line - 1].sum(&.size) + line + column - 2
|
||||||
|
pos += 1 if end_pos
|
||||||
|
pos
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,41 +18,92 @@ class Ameba::Source
|
||||||
@rewriter.replace(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
@rewriter.replace(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
def replace(range : Range(Int32, Int32), content)
|
||||||
|
begin_pos, end_pos = range.begin, range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.replace(begin_pos, end_pos, content)
|
||||||
|
end
|
||||||
|
|
||||||
# Inserts the given strings before and after the given range.
|
# Inserts the given strings before and after the given range.
|
||||||
def wrap(location, end_location, insert_before, insert_after)
|
def wrap(location, end_location, insert_before, insert_after)
|
||||||
@rewriter.wrap(loc_to_pos(location), loc_to_pos(end_location) + 1, insert_before, insert_after)
|
@rewriter.wrap(loc_to_pos(location), loc_to_pos(end_location) + 1, insert_before, insert_after)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
def wrap(range : Range(Int32, Int32), insert_before, insert_after)
|
||||||
|
begin_pos, end_pos = range.begin, range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.wrap(begin_pos, end_pos, insert_before, insert_after)
|
||||||
|
end
|
||||||
|
|
||||||
# Shortcut for `replace(location, end_location, "")`
|
# Shortcut for `replace(location, end_location, "")`
|
||||||
def remove(location, end_location)
|
def remove(location, end_location)
|
||||||
@rewriter.remove(loc_to_pos(location), loc_to_pos(end_location) + 1)
|
@rewriter.remove(loc_to_pos(location), loc_to_pos(end_location) + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Shortcut for `replace(range, "")`
|
||||||
|
def remove(range : Range(Int32, Int32))
|
||||||
|
begin_pos, end_pos = range.begin, range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.remove(begin_pos, end_pos)
|
||||||
|
end
|
||||||
|
|
||||||
# Shortcut for `wrap(location, end_location, content, nil)`
|
# Shortcut for `wrap(location, end_location, content, nil)`
|
||||||
def insert_before(location, end_location, content)
|
def insert_before(location, end_location, content)
|
||||||
@rewriter.insert_before(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
@rewriter.insert_before(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Shortcut for `wrap(range, content, nil)`
|
||||||
|
def insert_before(range : Range(Int32, Int32), content)
|
||||||
|
begin_pos, end_pos = range.begin, range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.insert_before(begin_pos, end_pos, content)
|
||||||
|
end
|
||||||
|
|
||||||
# Shortcut for `wrap(location, end_location, nil, content)`
|
# Shortcut for `wrap(location, end_location, nil, content)`
|
||||||
def insert_after(location, end_location, content)
|
def insert_after(location, end_location, content)
|
||||||
@rewriter.insert_after(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
@rewriter.insert_after(loc_to_pos(location), loc_to_pos(end_location) + 1, content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Shortcut for `wrap(range, nil, content)`
|
||||||
|
def insert_after(range : Range(Int32, Int32), content)
|
||||||
|
begin_pos, end_pos = range.begin, range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.insert_after(begin_pos, end_pos, content)
|
||||||
|
end
|
||||||
|
|
||||||
# Shortcut for `insert_before(location, location, content)`
|
# Shortcut for `insert_before(location, location, content)`
|
||||||
def insert_before(location, content)
|
def insert_before(location, content)
|
||||||
@rewriter.insert_before(loc_to_pos(location), content)
|
@rewriter.insert_before(loc_to_pos(location), content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Shortcut for `insert_before(pos.., content)`
|
||||||
|
def insert_before(pos : Int32, content)
|
||||||
|
@rewriter.insert_before(pos, content)
|
||||||
|
end
|
||||||
|
|
||||||
# Shortcut for `insert_after(location, location, content)`
|
# Shortcut for `insert_after(location, location, content)`
|
||||||
def insert_after(location, content)
|
def insert_after(location, content)
|
||||||
@rewriter.insert_after(loc_to_pos(location) + 1, content)
|
@rewriter.insert_after(loc_to_pos(location) + 1, content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Shortcut for `insert_after(...pos, content)`
|
||||||
|
def insert_after(pos : Int32, content)
|
||||||
|
@rewriter.insert_after(pos, content)
|
||||||
|
end
|
||||||
|
|
||||||
# Removes *size* characters prior to the source range.
|
# Removes *size* characters prior to the source range.
|
||||||
def remove_preceding(location, end_location, size)
|
def remove_preceding(location, end_location, size)
|
||||||
@rewriter.remove(loc_to_pos(location) - size, loc_to_pos(location))
|
@rewriter.remove(loc_to_pos(location) - size, loc_to_pos(location))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
def remove_preceding(range : Range(Int32, Int32), size)
|
||||||
|
begin_pos = range.begin
|
||||||
|
@rewriter.remove(begin_pos - size, begin_pos)
|
||||||
|
end
|
||||||
|
|
||||||
# Removes *size* characters from the beginning of the given range.
|
# Removes *size* characters from the beginning of the given range.
|
||||||
# If *size* is greater than the size of the range, the removed region can
|
# If *size* is greater than the size of the range, the removed region can
|
||||||
# overrun the end of the range.
|
# overrun the end of the range.
|
||||||
|
@ -60,6 +111,12 @@ class Ameba::Source
|
||||||
@rewriter.remove(loc_to_pos(location), loc_to_pos(location) + size)
|
@rewriter.remove(loc_to_pos(location), loc_to_pos(location) + size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
def remove_leading(range : Range(Int32, Int32), size)
|
||||||
|
begin_pos = range.begin
|
||||||
|
@rewriter.remove(begin_pos, begin_pos + size)
|
||||||
|
end
|
||||||
|
|
||||||
# Removes *size* characters from the end of the given range.
|
# Removes *size* characters from the end of the given range.
|
||||||
# If *size* is greater than the size of the range, the removed region can
|
# If *size* is greater than the size of the range, the removed region can
|
||||||
# overrun the beginning of the range.
|
# overrun the beginning of the range.
|
||||||
|
@ -67,6 +124,13 @@ class Ameba::Source
|
||||||
@rewriter.remove(loc_to_pos(end_location) + 1 - size, loc_to_pos(end_location) + 1)
|
@rewriter.remove(loc_to_pos(end_location) + 1 - size, loc_to_pos(end_location) + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
def remove_trailing(range : Range(Int32, Int32), size)
|
||||||
|
end_pos = range.end
|
||||||
|
end_pos -= 1 unless range.excludes_end?
|
||||||
|
@rewriter.remove(end_pos - size, end_pos)
|
||||||
|
end
|
||||||
|
|
||||||
private def loc_to_pos(location : Crystal::Location | {Int32, Int32})
|
private def loc_to_pos(location : Crystal::Location | {Int32, Int32})
|
||||||
if location.is_a?(Crystal::Location)
|
if location.is_a?(Crystal::Location)
|
||||||
line, column = location.line_number, location.column_number
|
line, column = location.line_number, location.column_number
|
||||||
|
|
Loading…
Reference in a new issue