mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Merge pull request #136 from crystal-ameba/feat/empty-loop
New rule: EmptyLoop
This commit is contained in:
commit
81631af631
3 changed files with 157 additions and 0 deletions
88
spec/ameba/rule/lint/empty_loop_spec.cr
Normal file
88
spec/ameba/rule/lint/empty_loop_spec.cr
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
require "../../../spec_helper"
|
||||||
|
|
||||||
|
module Ameba::Rule::Lint
|
||||||
|
describe EmptyLoop do
|
||||||
|
subject = EmptyLoop.new
|
||||||
|
|
||||||
|
it "does not report if there are not empty loops" do
|
||||||
|
s = Source.new %(
|
||||||
|
a = 1
|
||||||
|
|
||||||
|
while a < 10
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
until a == 10
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
loop do
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports if there is an empty while loop" do
|
||||||
|
s = Source.new %(
|
||||||
|
a = 1
|
||||||
|
while true
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't report if while loop has non-literals in cond block" do
|
||||||
|
s = Source.new %(
|
||||||
|
a = 1
|
||||||
|
while a = gets.to_s
|
||||||
|
# nothing here
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports if there is an empty until loop" do
|
||||||
|
s = Source.new %(
|
||||||
|
do_something
|
||||||
|
until false
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't report if until loop has non-literals in cond block" do
|
||||||
|
s = Source.new %(
|
||||||
|
until socket_open?
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports if there an empty loop" do
|
||||||
|
s = Source.new %(
|
||||||
|
a = 1
|
||||||
|
loop do
|
||||||
|
|
||||||
|
end
|
||||||
|
)
|
||||||
|
subject.catch(s).should_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports rule, message and location" do
|
||||||
|
s = Source.new %(
|
||||||
|
a = 1
|
||||||
|
loop do
|
||||||
|
# comment goes here
|
||||||
|
end
|
||||||
|
), "source.cr"
|
||||||
|
subject.catch(s).should_not be_valid
|
||||||
|
s.issues.size.should eq 1
|
||||||
|
issue = s.issues.first
|
||||||
|
issue.rule.should_not be_nil
|
||||||
|
issue.location.to_s.should eq "source.cr:2:1"
|
||||||
|
issue.end_location.to_s.should eq "source.cr:4:3"
|
||||||
|
issue.message.should eq EmptyLoop::MSG
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,6 +25,7 @@ module Ameba::AST
|
||||||
Var,
|
Var,
|
||||||
When,
|
When,
|
||||||
While,
|
While,
|
||||||
|
Until,
|
||||||
]
|
]
|
||||||
|
|
||||||
# An AST Visitor that traverses the source and allows all nodes
|
# An AST Visitor that traverses the source and allows all nodes
|
||||||
|
|
68
src/ameba/rule/lint/empty_loop.cr
Normal file
68
src/ameba/rule/lint/empty_loop.cr
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
module Ameba::Rule::Lint
|
||||||
|
# A rule that disallows empty loops.
|
||||||
|
#
|
||||||
|
# This is considered invalid:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# while false
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# until 10
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# loop do
|
||||||
|
# # nothing here
|
||||||
|
# end
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# And this is valid:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# a = 1
|
||||||
|
# while a < 10
|
||||||
|
# a += 1
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# until socket_opened?
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# loop do
|
||||||
|
# do_something_here
|
||||||
|
# end
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# Lint/EmptyLoop:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
struct EmptyLoop < Base
|
||||||
|
include AST::Util
|
||||||
|
|
||||||
|
properties do
|
||||||
|
description "Disallows empty loops"
|
||||||
|
end
|
||||||
|
|
||||||
|
MSG = "Empty loop detected"
|
||||||
|
|
||||||
|
def test(source, node : Crystal::Call)
|
||||||
|
return unless loop?(node)
|
||||||
|
|
||||||
|
check_node(source, node, node.block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test(source, node : Crystal::While)
|
||||||
|
check_node(source, node, node.body) if literal?(node.cond)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test(source, node : Crystal::Until)
|
||||||
|
check_node(source, node, node.body) if literal?(node.cond)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def check_node(source, node, loop_body)
|
||||||
|
body = loop_body.is_a?(Crystal::Block) ? loop_body.body : loop_body
|
||||||
|
issue_for node, MSG if body.nil? || body.nop?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue