mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
940 lines
22 KiB
Crystal
940 lines
22 KiB
Crystal
require "../../../spec_helper"
|
|
|
|
module Ameba::Rule::Lint
|
|
describe UselessAssign do
|
|
subject = UselessAssign.new
|
|
|
|
it "does not report used assigments" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports a useless assignment in a method" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports a useless assignment in a proc" do
|
|
s = Source.new %(
|
|
->() {
|
|
a = 2
|
|
}
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports a useless assignment in a block" do
|
|
s = Source.new %(
|
|
def method
|
|
3.times do
|
|
a = 1
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports a useless assignment in a proc inside def" do
|
|
s = Source.new %(
|
|
def method
|
|
->() {
|
|
a = 2
|
|
}
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports a useless assignment in a proc inside a block" do
|
|
s = Source.new %(
|
|
def method
|
|
3.times do
|
|
->() {
|
|
a = 2
|
|
}
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports rule, position and a message" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
end
|
|
), "source.cr"
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.rule.should_not be_nil
|
|
issue.location.to_s.should eq "source.cr:2:3"
|
|
issue.message.should eq "Useless assignment to variable `a`"
|
|
end
|
|
|
|
it "does not report useless assignment of instance var" do
|
|
s = Source.new %(
|
|
class Cls
|
|
def initialize(@name)
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if assignment used in the inner block scope" do
|
|
s = Source.new %(
|
|
def method
|
|
var = true
|
|
3.times { var = false }
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assigned is not referenced in the inner block scope" do
|
|
s = Source.new %(
|
|
def method
|
|
var = true
|
|
3.times {}
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment in referenced in inner block" do
|
|
s = Source.new %(
|
|
def method
|
|
two = true
|
|
|
|
3.times do
|
|
mutex.synchronize do
|
|
two = 2
|
|
end
|
|
end
|
|
|
|
two.should be_true
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if first assignment is useless" do
|
|
s = Source.new %(
|
|
def method
|
|
var = true
|
|
var = false
|
|
var
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.first.location.to_s.should eq ":2:3"
|
|
end
|
|
|
|
it "reports if variable reassigned and not used" do
|
|
s = Source.new %(
|
|
def method
|
|
var = true
|
|
var = false
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "does not report if variable used in a condition" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 1
|
|
if a
|
|
nil
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports second assignment as useless" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 1
|
|
a = a + 1
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "does not report if variable is referenced in other assignment" do
|
|
s = Source.new %(
|
|
def method
|
|
if f = get_something
|
|
@f = f
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if variable is referenced in a setter" do
|
|
s = Source.new %(
|
|
def method
|
|
foo = 2
|
|
table[foo] ||= "bar"
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if variable is reassigned but not referenced" do
|
|
s = Source.new %(
|
|
def method
|
|
foo = 1
|
|
puts foo
|
|
foo = 2
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "does not report if variable is referenced in a call" do
|
|
s = Source.new %(
|
|
def method
|
|
if f = FORMATTER
|
|
@formatter = f.new
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if a setter is invoked with operator assignment" do
|
|
s = Source.new %(
|
|
def method
|
|
obj = {} of Symbol => Int32
|
|
obj[:name] = 3
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if global var" do
|
|
s = Source.new %(
|
|
def method
|
|
$? = 3
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if assignment is referenced in a proc" do
|
|
s = Source.new %(
|
|
def method
|
|
called = false
|
|
->() { called = true }
|
|
called
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if variable is shadowed in inner scope" do
|
|
s = Source.new %(
|
|
def method
|
|
i = 1
|
|
3.times do |i|
|
|
i + 1
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "does not report if parameter is referenced after the branch" do
|
|
s = Source.new %(
|
|
def method(param)
|
|
3.times do
|
|
param = 3
|
|
end
|
|
param
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
context "op assigns" do
|
|
it "does not report if variable is referenced below the op assign" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 1
|
|
a += 1
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if variable is referenced in op assign few times" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 1
|
|
a += 1
|
|
a += 1
|
|
a = a + 1
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if variable is not referenced below the op assign" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 1
|
|
a += 1
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports rule, location and a message" do
|
|
s = Source.new %(
|
|
def method
|
|
b = 2
|
|
a = 3
|
|
a += 1
|
|
end
|
|
), "source.cr"
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.last
|
|
issue.rule.should_not be_nil
|
|
issue.location.to_s.should eq "source.cr:4:3"
|
|
issue.message.should eq "Useless assignment to variable `a`"
|
|
end
|
|
end
|
|
|
|
context "multi assigns" do
|
|
it "does not report if all assigns are referenced" do
|
|
s = Source.new %(
|
|
def method
|
|
a, b = {1, 2}
|
|
a + b
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if one assign is not referenced" do
|
|
s = Source.new %(
|
|
def method
|
|
a, b = {1, 2}
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":2:6"
|
|
issue.message.should eq "Useless assignment to variable `b`"
|
|
end
|
|
|
|
it "reports if both assigns are reassigned and useless" do
|
|
s = Source.new %(
|
|
def method
|
|
a, b = {1, 2}
|
|
a, b = {3, 4}
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports if both assigns are not referenced" do
|
|
s = Source.new %(
|
|
def method
|
|
a, b = {1, 2}
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":2:3"
|
|
issue.message.should eq "Useless assignment to variable `a`"
|
|
|
|
issue = s.issues.last
|
|
issue.location.to_s.should eq ":2:6"
|
|
issue.message.should eq "Useless assignment to variable `b`"
|
|
end
|
|
end
|
|
|
|
context "top level" do
|
|
it "reports if assignment is not referenced" do
|
|
s = Source.new %(
|
|
a = 1
|
|
a = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 2
|
|
s.issues.first.location.to_s.should eq ":1:1"
|
|
s.issues.last.location.to_s.should eq ":2:1"
|
|
end
|
|
|
|
it "doesn't report if assignments are referenced" do
|
|
s = Source.new %(
|
|
a = 1
|
|
a += 1
|
|
a
|
|
|
|
b, c = {1, 2}
|
|
b
|
|
c
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is captured by block" do
|
|
s = Source.new %(
|
|
a = 1
|
|
|
|
3.times do
|
|
a = 2
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment initialized and captured by block" do
|
|
s = Source.new %(
|
|
a : String? = nil
|
|
|
|
1.times do
|
|
a = "Fotis"
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if this is a record declaration" do
|
|
s = Source.new %(
|
|
record Foo, foo = "foo"
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if assignment is referenced after the record declaration" do
|
|
s = Source.new %(
|
|
foo = 2
|
|
record Bar, foo = 3 # foo = 3 is not parsed as assignment
|
|
puts foo
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is not referenced after the record declaration" do
|
|
s = Source.new %(
|
|
foo = 2
|
|
record Bar, foo = 3
|
|
), "source.cr"
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq "source.cr:1:1"
|
|
issue.message.should eq "Useless assignment to variable `foo`"
|
|
end
|
|
end
|
|
|
|
context "branching" do
|
|
context "if-then-else" do
|
|
it "doesn't report if assignment is consumed by branches" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 0
|
|
if something
|
|
a = 1
|
|
else
|
|
a = 2
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is in one branch" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 0
|
|
if something
|
|
a = 1
|
|
else
|
|
nil
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is in one line branch" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 0
|
|
a = 1 if something
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless in the branch" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
if a
|
|
a = 2
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports if only last assignment is referenced in a branch" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
a = 1
|
|
if a
|
|
a = 2
|
|
a = 3
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":4:5"
|
|
end
|
|
|
|
it "does not report of assignments are referenced in all branches" do
|
|
s = Source.new %(
|
|
def method
|
|
if matches
|
|
matches = owner.lookup_matches signature
|
|
else
|
|
matches = owner.lookup_matches signature
|
|
end
|
|
|
|
matches
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report referenced assignments in inner branches" do
|
|
s = Source.new %(
|
|
def method
|
|
has_newline = false
|
|
|
|
if something
|
|
do_something unless false
|
|
has_newline = false
|
|
else
|
|
do_something if true
|
|
has_newline = true
|
|
end
|
|
|
|
has_newline
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "unless-then-else" do
|
|
it "doesn't report if assignment is consumed by branches" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 0
|
|
unless something
|
|
a = 1
|
|
else
|
|
a = 2
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if there is a useless assignment in a branch" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 0
|
|
unless something
|
|
a = 1
|
|
a = 2
|
|
else
|
|
a = 2
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":4:5"
|
|
end
|
|
end
|
|
|
|
context "case" do
|
|
it "does not report if assignment is referenced" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
case a
|
|
when /foo/
|
|
a = 1
|
|
when /bar/
|
|
a = 2
|
|
end
|
|
puts a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
case a
|
|
when /foo/
|
|
a = 1
|
|
when /bar/
|
|
a = 2
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 2
|
|
s.issues.first.location.to_s.should eq ":4:5"
|
|
s.issues.last.location.to_s.should eq ":6:5"
|
|
end
|
|
|
|
it "doesn't report if assignment is referenced in cond" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
case a
|
|
when /foo/
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "binary operator" do
|
|
it "does not report if assignment is referenced" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
(a = 1) && (b = 1)
|
|
a + b
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
(a = 1) || (b = 1)
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":2:15"
|
|
end
|
|
end
|
|
|
|
context "while" do
|
|
it "does not report if assignment is referenced" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
while a < 10
|
|
a = a + 1
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
while a < 10
|
|
b = a
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":3:5"
|
|
end
|
|
|
|
it "does not report if assignment is referenced in a loop" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 3
|
|
result = 0
|
|
|
|
while result < 10
|
|
result += a
|
|
a = a + 1
|
|
end
|
|
result
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if assignment is referenced as param in a loop" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
result = 0
|
|
|
|
while result < 10
|
|
result += a
|
|
a = a + 1
|
|
end
|
|
result
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if assignment is referenced in loop and inner branch" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
result = 0
|
|
|
|
while result < 10
|
|
result += a
|
|
if result > 0
|
|
a = a + 1
|
|
else
|
|
a = 3
|
|
end
|
|
end
|
|
result
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "works properly if there is branch with blank node" do
|
|
s = Source.new %(
|
|
def visit
|
|
count = 0
|
|
while true
|
|
break if count == 1
|
|
case something
|
|
when :any
|
|
else
|
|
:anything_else
|
|
end
|
|
count += 1
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "until" do
|
|
it "does not report if assignment is referenced" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
until a > 10
|
|
a = a + 1
|
|
end
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
until a > 10
|
|
b = a + 1
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":3:5"
|
|
end
|
|
end
|
|
|
|
context "exception handler" do
|
|
it "does not report if assignment is referenced in body" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
a = 2
|
|
rescue
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is referenced in ensure" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
a = 2
|
|
ensure
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is referenced in else" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
a = 2
|
|
rescue
|
|
else
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is useless" do
|
|
s = Source.new %(
|
|
def method(a)
|
|
rescue
|
|
a = 2
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
s.issues.size.should eq 1
|
|
s.issues.first.location.to_s.should eq ":3:3"
|
|
end
|
|
end
|
|
end
|
|
|
|
context "macro" do
|
|
it "doesn't report if assignment is referenced in macro" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
{% if flag?(:bits64) %}
|
|
a.to_s
|
|
{% else %}
|
|
a
|
|
{% end %}
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report referenced assignments in macro literal" do
|
|
s = Source.new %(
|
|
def method
|
|
a = 2
|
|
{% if flag?(:bits64) %}
|
|
a = 3
|
|
{% else %}
|
|
a = 4
|
|
{% end %}
|
|
puts a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is referenced in macro def" do
|
|
s = Source.new %(
|
|
macro macro_call
|
|
puts x
|
|
end
|
|
|
|
def foo
|
|
x = 1
|
|
macro_call
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if assignment is referenced in macro def in a different scope" do
|
|
s = Source.new %(
|
|
class Foo
|
|
def foo
|
|
x = 1
|
|
end
|
|
end
|
|
|
|
class Bar
|
|
macro macro_call
|
|
puts x
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "doesn't report if assignment is referenced in a macro below" do
|
|
s = Source.new %(
|
|
class Foo
|
|
def foo
|
|
a = 1
|
|
macro_call
|
|
end
|
|
|
|
macro macro_call
|
|
puts a
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "uninitialized" do
|
|
it "reports if uninitialized assignment is not referenced at a top level" do
|
|
s = Source.new %(
|
|
a = uninitialized U
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "reports if uninitialized assignment is not referenced in a method" do
|
|
s = Source.new %(
|
|
def foo
|
|
a = uninitialized U
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
end
|
|
|
|
it "doesn't report if uninitialized assignment is referenced" do
|
|
s = Source.new %(
|
|
def foo
|
|
a = uninitialized U
|
|
a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
end
|
|
end
|