Unused argument rule (#52)

* Unused argument rule

* IgnoreDefs, IgnoreBlocks, IgnoreProcs parameters

* Implicit reference by super keyworkd

* Handle macro arguments
This commit is contained in:
V. Elenhaupt 2018-05-08 22:00:17 +03:00 committed by GitHub
parent cc71511080
commit c2aa526e21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 467 additions and 22 deletions

View file

@ -0,0 +1,40 @@
require "../../../spec_helper"
module Ameba::AST
describe Argument do
arg = Crystal::Arg.new "a"
scope = Scope.new as_node "foo = 1"
variable = Variable.new(Crystal::Var.new("foo"), scope)
describe "#initialize" do
it "creates a new argument" do
argument = Argument.new(arg, variable)
argument.node.should_not be_nil
end
end
describe "delegation" do
it "delegates location to node" do
argument = Argument.new(arg, variable)
argument.location.should eq arg.location
end
it "delegates to_s to node" do
argument = Argument.new(arg, variable)
argument.to_s.should eq arg.to_s
end
end
describe "#ignored?" do
it "is true if arg starts with _" do
argument = Argument.new(Crystal::Arg.new("_a"), variable)
argument.ignored?.should be_true
end
it "is false otherwise" do
argument = Argument.new(arg, variable)
argument.ignored?.should be_false
end
end
end
end

View file

@ -0,0 +1,259 @@
require "../../spec_helper"
module Ameba::Rule
subject = UnusedArgument.new
subject.ignore_defs = false
describe UnusedArgument do
it "doesn't report if arguments are used" do
s = Source.new %(
def method(a, b, c)
a + b + c
end
3.times do |i|
i + 1
end
->(i : Int32) { i + 1 }
)
subject.catch(s).should be_valid
end
it "reports if method argument is unused" do
s = Source.new %(
def method(a, b, c)
a + b
end
)
subject.catch(s).should_not be_valid
s.errors.first.message.should eq "Unused argument `c`"
end
it "reports if block argument is unused" do
s = Source.new %(
[1,2].each_with_index do |a, i|
a
end
)
subject.catch(s).should_not be_valid
s.errors.first.message.should eq "Unused argument `i`"
end
it "reports if proc argument is unused" do
s = Source.new %(
-> (a : Int32, b : String) do
a = a + 1
end
)
subject.catch(s).should_not be_valid
s.errors.first.message.should eq "Unused argument `b`"
end
it "reports multiple unused args" do
s = Source.new %(
def method(a, b, c)
nil
end
)
subject.catch(s).should_not be_valid
s.errors[0].message.should eq "Unused argument `a`"
s.errors[1].message.should eq "Unused argument `b`"
s.errors[2].message.should eq "Unused argument `c`"
end
it "doesn't report if it is an instance var argument" do
s = Source.new %(
class A
def method(@name)
end
end
)
subject.catch(s).should be_valid
end
it "doesn't report if a typed argument is used" do
s = Source.new %(
def method(x : Int32)
3.times do
puts x
end
end
)
subject.catch(s).should be_valid
end
it "doesn't report if an argument with default value is used" do
s = Source.new %(
def method(x = 1)
puts x
end
)
subject.catch(s).should be_valid
end
it "doesn't report if argument starts with a _" do
s = Source.new %(
def method(_x)
end
)
subject.catch(s).should be_valid
end
it "doesn't report if it is a block and used" do
s = Source.new %(
def method(&block)
block.call
end
)
subject.catch(s).should be_valid
end
it "reports if block arg is not used" do
s = Source.new %(
def method(&block)
end
)
subject.catch(s).should_not be_valid
end
it "reports if unused and there is yield" do
s = Source.new %(
def method(&block)
yield 1
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if variable is referenced implicitly" do
s = Source.new %(
class Bar < Foo
def method(a, b)
super
end
end
)
subject.catch(s).should be_valid
end
context "super" do
it "reports if variable is not referenced implicitly by super" do
s = Source.new %(
class Bar < Foo
def method(a, b)
super a
end
end
)
subject.catch(s).should_not be_valid
s.errors.first.message.should eq "Unused argument `b`"
end
it "reports rule, location and message" do
s = Source.new %(
def method(a)
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.message.should eq "Unused argument `a`"
error.location.to_s.should eq "source.cr:2:22"
end
end
context "macro" do
it "doesn't report if it is a used macro argument" do
s = Source.new %(
macro my_macro(arg)
{% arg %}
end
)
subject.catch(s).should be_valid
end
it "doesn't report if it is a used macro block argument" do
s = Source.new %(
macro my_macro(&block)
{% block %}
end
)
subject.catch(s).should be_valid
end
it "doesn't report used macro args with equal names in record" do
s = Source.new %(
record X do
macro foo(a, b)
{{a}} + {{b}}
end
macro bar(a, b, c)
{{a}} + {{b}} + {{c}}
end
end
)
subject.catch(s).should be_valid
end
end
context "properties" do
describe "#ignore_defs" do
it "lets the rule to ignore def scopes if true" do
subject.ignore_defs = true
s = Source.new %(
def method(a)
end
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore def scopes if false" do
subject.ignore_defs = false
s = Source.new %(
def method(a)
end
)
subject.catch(s).should_not be_valid
end
end
context "#ignore_blocks" do
it "lets the rule to ignore block scopes if true" do
subject.ignore_blocks = true
s = Source.new %(
3.times { |i| puts "yo!" }
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore block scopes if false" do
subject.ignore_blocks = false
s = Source.new %(
3.times { |i| puts "yo!" }
)
subject.catch(s).should_not be_valid
end
end
context "#ignore_procs" do
it "lets the rule to ignore proc scopes if true" do
subject.ignore_procs = true
s = Source.new %(
->(a : Int32) {}
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore proc scopes if false" do
subject.ignore_procs = false
s = Source.new %(
->(a : Int32) {}
)
subject.catch(s).should_not be_valid
end
end
end
end
end