Merge pull request #327 from crystal-ameba/Sija/specs-cleanup

This commit is contained in:
Sijawusz Pur Rahnama 2022-12-22 12:08:44 +01:00 committed by GitHub
commit b9bc5aaab2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 354 additions and 841 deletions

View file

@ -10,29 +10,29 @@ module Ameba::AST
describe ".of" do
context "Crystal::If" do
it "constructs a branch in If.cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if a = get_something # --> Crystal::Assign
puts a
end
end
)
CRYSTAL
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in If.then" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if true
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in If.else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if true
nil
@ -40,45 +40,45 @@ module Ameba::AST
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline If" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
a = 0 if a == 2 # --> Crystal::Assign
end
)
CRYSTAL
branch.to_s.should eq "a = 0"
end
end
context "Crystal::Unless" do
it "constructs a branch in Unless.cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless a = get_something # --> Crystal::Assign
puts a
end
end
)
CRYSTAL
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in Unless.then" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless true
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a new branch in Unless.else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless true
nil
@ -86,188 +86,188 @@ module Ameba::AST
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline Unless" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
(a = 0; b = 3) unless a == 2 # --> Crystal::Expressions
end
)
CRYSTAL
branch.to_s.should eq "(a = 0\nb = 3)"
end
end
context "Crystal::BinaryOp" do
it "constructs a branch in left node" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
(a = 2) && do_something
end
)
CRYSTAL
branch.to_s.should eq "(a = 2)"
end
it "constructs a branch in right node" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
do_something || (a = 0)
end
)
CRYSTAL
branch.to_s.should eq "(a = 0)"
end
end
context "Crystal::Case" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case (a = 2)
when true then nil
end
end
)
CRYSTAL
branch.to_s.should eq "(a = 2)"
end
it "constructs a branch in when" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case a
when a = 3 then nil
end
end
)
CRYSTAL
branch.to_s.should eq "when a = 3\n nil\n"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case a
when true then nil
else a = 4
end
end
)
CRYSTAL
branch.to_s.should eq "a = 4"
end
end
context "Crystal::While" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
while a = 1
nil
end
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
while true
b = (a = 1)
end
end
)
CRYSTAL
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::Until" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
until a = 1
nil
end
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
until false
b = (a = 1)
end
end
)
CRYSTAL
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::ExceptionHandler" do
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
a = 1
rescue
nil
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in a rescue" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
else
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in ensure" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
ensure
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
end
context "Crystal::MacroIf" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
{% if a = 2 %}
{% end %}
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in then" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
{% if true %}
a = 2
{% end %}
end
)
CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
@ -275,24 +275,24 @@ module Ameba::AST
context "Crystal::MacroFor" do
it "constructs a branch in body" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
{% for x in [1, 2, 3] %}
a = 2
{% end %}
end
)
CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
end
it "returns nil if branch does not exist" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
end
)
CRYSTAL
branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first)
branch.should be_nil
end
@ -300,11 +300,11 @@ module Ameba::AST
describe "#initialize" do
it "creates new branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.node.should_not be_nil
@ -313,22 +313,22 @@ module Ameba::AST
describe "delegation" do
it "delegates to_s to node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.to_s.should eq branch.node.to_s
end
it "delegates locations to node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.location.should eq branch.node.location
@ -338,22 +338,22 @@ module Ameba::AST
describe "#in_loop?" do
it "returns true if branch is in a loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
while true
a = 1
end
)
CRYSTAL
branchable = Branchable.new nodes.while_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_true
end
it "returns false if branch is not in a loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if a > 2
a = 1
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_false

View file

@ -4,20 +4,20 @@ module Ameba::AST
describe Branchable do
describe "#initialize" do
it "creates a new branchable" do
branchable = Branchable.new as_node %(a = 2 if true)
branchable = Branchable.new as_node "a = 2 if true"
branchable.node.should_not be_nil
end
end
describe "delegation" do
it "delegates to_s to @node" do
node = as_node %(a = 2 if true)
node = as_node "a = 2 if true"
branchable = Branchable.new node
branchable.to_s.should eq node.to_s
end
it "delegates locations to @node" do
node = as_node %(a = 2 if true)
node = as_node "a = 2 if true"
branchable = Branchable.new node
branchable.location.should eq node.location
branchable.end_location.should eq node.end_location
@ -26,22 +26,22 @@ module Ameba::AST
describe "#loop?" do
it "returns true if it is a while loop" do
branchable = Branchable.new as_node %(while true; a = 2; end)
branchable = Branchable.new as_node "while true; a = 2; end"
branchable.loop?.should be_true
end
it "returns true if it is the until loop" do
branchable = Branchable.new as_node %(until false; a = 2; end)
branchable = Branchable.new as_node "until false; a = 2; end"
branchable.loop?.should be_true
end
it "returns true if it is loop" do
branchable = Branchable.new as_node %(loop {})
branchable = Branchable.new as_node "loop {}"
branchable.loop?.should be_true
end
it "returns false otherwise" do
branchable = Branchable.new as_node %(a = 2 if true)
branchable = Branchable.new as_node "a = 2 if true"
branchable.loop?.should be_false
end
end

View file

@ -18,7 +18,7 @@ module Ameba::AST
end
it "delegates locations to @node" do
node = as_node %(break if true)
node = as_node("break if true")
flow_expression = FlowExpression.new node, false
flow_expression.location.should eq node.location
flow_expression.end_location.should eq node.end_location
@ -27,20 +27,20 @@ module Ameba::AST
describe "#unreachable_nodes" do
it "returns unreachable nodes" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def foobar
return
a = 1
a = 2
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.should eq nodes.assign_nodes
end
it "returns nil if there is no unreachable node after loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def run
idx = items.size - 1
while 0 <= idx
@ -49,19 +49,19 @@ module Ameba::AST
puts "foo"
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true
end
it "returns nil if there is no unreachable node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def foobar
a = 1
return a
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true

View file

@ -49,14 +49,14 @@ module Ameba::AST
describe "#references?" do
it "returns true if current scope references variable" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
3.times { |i| a = a + i }
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first
scope.add_variable var_node
@ -69,14 +69,14 @@ module Ameba::AST
end
it "returns false if inner scopes are not checked" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
3.times { |i| a = a + i }
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first
scope.add_variable var_node
@ -89,7 +89,7 @@ module Ameba::AST
end
it "returns false if current scope does not reference variable" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
@ -97,7 +97,7 @@ module Ameba::AST
3.times { |i| b = b + i }
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first
scope.add_variable var_node
@ -150,57 +150,53 @@ module Ameba::AST
describe "#block?" do
it "returns true if Crystal::Block" do
nodes = as_nodes %(
3.times {}
)
nodes = as_nodes("3.times {}")
scope = Scope.new nodes.block_nodes.first
scope.block?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.block?.should be_false
end
end
describe "#spawn_block?" do
it "returns true if a node is a spawn block" do
nodes = as_nodes %(
spawn {}
)
nodes = as_nodes("spawn {}")
scope = Scope.new nodes.block_nodes.first
scope.spawn_block?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.spawn_block?.should be_false
end
end
describe "#in_macro?" do
it "returns true if Crystal::Macro" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
macro included
end
)
CRYSTAL
scope = Scope.new nodes.macro_nodes.first
scope.in_macro?.should be_true
end
it "returns true if node is nested to Crystal::Macro" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
macro included
{{ @type.each do |type| a = type end }}
end
)
CRYSTAL
outer_scope = Scope.new nodes.macro_nodes.first
scope = Scope.new nodes.block_nodes.first, outer_scope
scope.in_macro?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.in_macro?.should be_false
end
end

View file

@ -41,15 +41,14 @@ module Ameba::AST
describe "#branch" do
it "returns the branch of the assignment" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
if a
a = 3 # --> Crystal::Expressions
puts a
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -58,7 +57,7 @@ module Ameba::AST
end
it "returns inner branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a, b)
if a
if b
@ -66,7 +65,7 @@ module Ameba::AST
end
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -75,12 +74,11 @@ module Ameba::AST
end
it "returns nil if assignment does not have a branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
a = 2
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -90,12 +88,11 @@ module Ameba::AST
describe "#transformed?" do
it "returns false if the assignment is not transformed by the compiler" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
a = 2
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -103,11 +100,10 @@ module Ameba::AST
end
it "returns true if the assignment is transformed by the compiler" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
array.each do |(a, b)|
end
)
CRYSTAL
scope = Scope.new nodes.block_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)

View file

@ -79,12 +79,12 @@ module Ameba::AST
describe "#captured_by_block?" do
it "returns truthy if the variable is captured by block" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
3.times { |i| a = a + i }
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first
scope.add_variable var_node
@ -95,12 +95,12 @@ module Ameba::AST
variable.captured_by_block?.should be_truthy
end
it "returns falsey if the variable is not captured by the block" do
scope = Scope.new as_node %(
it "returns falsy if the variable is not captured by the block" do
scope = Scope.new as_node <<-CRYSTAL
def method
a = 1
end
)
CRYSTAL
scope.add_variable Crystal::Var.new "a"
variable = scope.variables.first
variable.captured_by_block?.should be_falsey

View file

@ -19,33 +19,33 @@ module Ameba::AST
end
it "is 1 if there is Macro::For" do
code = %(
def initialize()
code = <<-CRYSTAL
def initialize
{% for c in ALL_NODES %}
true || false
{% end %}
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 1
end
it "is 1 if there is Macro::If" do
code = %(
def initialize()
code = <<-CRYSTAL
def initialize
{% if foo.bar? %}
true || false
{% end %}
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 1
end
it "increases count for every exhaustive case" do
code = %(
code = <<-CRYSTAL
def hello(a : Int32 | Int64 | Float32 | Float64)
case a
in Int32 then "int32"
@ -54,7 +54,7 @@ module Ameba::AST
in Float64 then "float64"
end
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 2

View file

@ -6,17 +6,17 @@ module Ameba::AST
describe FlowExpressionVisitor do
it "creates an expression for return" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
return :bar
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
it "can create multiple expressions" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
if bar
return :baz
@ -24,42 +24,42 @@ module Ameba::AST
return :foobar
end
end
)
CRYSTAL
rule.expressions.size.should eq 3
end
it "properly creates nested flow expressions" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
return(
return (
loop do
break if a > 1
return a
end
)
end
)
CRYSTAL
rule.expressions.size.should eq 4
end
it "creates an expression for break" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true
break
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
it "creates an expression for next" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true
next if something
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
end

View file

@ -5,11 +5,11 @@ module Ameba::AST
rule = RedundantControlExpressionRule.new
describe RedundantControlExpressionVisitor do
node = as_node %(
node = as_node <<-CRYSTAL
a = 1
b = 2
return a + b
)
CRYSTAL
subject = RedundantControlExpressionVisitor.new(rule, source, node)
it "assigns valid attributes" do

View file

@ -4,37 +4,37 @@ module Ameba::AST
describe ScopeVisitor do
it "creates a scope for the def" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method
end
)
CRYSTAL
rule.scopes.size.should eq 1
end
it "creates a scope for the proc" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
-> {}
)
CRYSTAL
rule.scopes.size.should eq 1
end
it "creates a scope for the block" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times {}
)
CRYSTAL
rule.scopes.size.should eq 2
end
context "inner scopes" do
it "creates scope for block inside def" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method
3.times {}
end
)
CRYSTAL
rule.scopes.size.should eq 2
rule.scopes.last.outer_scope.should_not be_nil
rule.scopes.first.outer_scope.should eq rule.scopes.last
@ -42,11 +42,11 @@ module Ameba::AST
it "creates scope for block inside block" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times do
2.times {}
end
)
CRYSTAL
rule.scopes.size.should eq 3
inner_block = rule.scopes.first
outer_block = rule.scopes.last

View file

@ -4,10 +4,12 @@ module Ameba::AST
describe TopLevelNodesVisitor do
describe "#require_nodes" do
it "returns require node" do
source = Source.new %(
source = Source.new <<-CRYSTAL
require "foo"
def bar; end
)
def bar
end
CRYSTAL
visitor = TopLevelNodesVisitor.new(source.ast)
visitor.require_nodes.size.should eq 1
visitor.require_nodes.first.to_s.should eq %q(require "foo")

View file

@ -26,135 +26,135 @@ module Ameba
end
it "disables a rule with a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
source.should be_valid
end
it "disables a rule with a line that ends with a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
Time.epoch(1483859302) # ameba:disable #{NamedRule.name}
)
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
source.should be_valid
end
it "does not disable a rule of a different name" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable WrongName
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "Error!")
source.should_not be_valid
end
it "disables a rule if multiple rule names provided" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should be_valid
end
it "disables a rule if multiple rule names are separated by comma" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should be_valid
end
it "does not disable if multiple rule names used without required one" do
s = Source.new %(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, SomeOtherRule LargeNumbers
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
it "does not disable if comment directive has wrong place" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name}
#
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if comment directive added to the wrong line" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
if use_epoch? # ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
end
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is not a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
"ameba:disable #{NamedRule.name}"
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is a commented out directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# # ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is an inline commented out directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # Disable it: # ameba:disable #{NamedRule.name}
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
context "with group name" do
it "disables one rule with a group" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name}
)
s.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {1, 12}, message: "")
source.should be_valid
end
it "doesn't disable others rules" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name}
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
it "disables a hole group of rules" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.group_name}
)
s.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {1, 12}, message: "")
source.should be_valid
end
it "does not disable rules which do not belong to the group" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable Lint
)
s.add_issue(DummyRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
end
end

View file

@ -34,7 +34,7 @@ module Ameba::Rule::Layout
end
context "properties" do
it "allows to configure max length of the line" do
it "#max_length" do
rule = LineLength.new
rule.max_length = long_line.size

View file

@ -5,7 +5,7 @@ module Ameba::Rule::Layout
describe TrailingWhitespace do
it "passes if all lines do not have trailing whitespace" do
expect_no_issues subject, "no-whispace"
expect_no_issues subject, "no-whitespace"
end
it "fails if there is a line with trailing whitespace" do
@ -15,16 +15,5 @@ module Ameba::Rule::Layout
expect_correction source, "whitespace at the end"
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n b = 2 ", "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:7"
issue.end_location.to_s.should eq "source.cr:2:7"
issue.message.should eq "Trailing whitespace detected"
end
end
end

View file

@ -94,16 +94,6 @@ module Ameba::Rule::Lint
a
CRYSTAL
end
it "reports rule, pos and message" do
source = Source.new "a != true", "source.cr"
subject.catch(source)
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
context "boolean on the left" do
@ -165,17 +155,6 @@ module Ameba::Rule::Lint
a
CRYSTAL
end
it "reports rule, pos and message" do
source = Source.new "true != a", "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:9"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
end
end

View file

@ -28,16 +28,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new "pp! :foo", "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:8"
issue.message.should eq "Possibly forgotten debug-related `pp!` call detected"
end
end
end

View file

@ -31,16 +31,5 @@ module Ameba::Rule::Lint
expect_no_corrections source
end
it "reports rule, pos and message" do
s = Source.new "debugger", "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:8"
issue.message.should eq "Possible forgotten debugger statement detected"
end
end
end

View file

@ -22,28 +22,5 @@ module Ameba::Rule::Lint
expect_no_corrections source
end
it "reports rule, pos and message" do
source = Source.new %(
require "./thing"
require "./thing"
require "./another_thing"
require "./another_thing"
), "source.cr"
subject.catch(source).should_not be_valid
issue = source.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 ""
issue.message.should eq "Duplicated require of `./thing`"
issue = source.issues.last
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:4:1"
issue.end_location.to_s.should eq ""
issue.message.should eq "Duplicated require of `./another_thing`"
end
end
end

View file

@ -3,17 +3,17 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Lint::EmptyExpression.new
def it_detects_empty_expression(code)
it "detects empty expression" do
private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__)
it %(detects empty expression "#{code}"), file, line do
s = Source.new code
rule = Rule::Lint::EmptyExpression.new
rule.catch(s).should_not be_valid
rule.catch(s).should_not be_valid, file: file, line: line
end
end
describe Rule::Lint::EmptyExpression do
it "passes if there is no empty expression" do
s = Source.new %(
s = Source.new <<-CRYSTAL
def method()
end
@ -30,7 +30,7 @@ module Ameba
begin "" end
[nil] << nil
)
CRYSTAL
subject.catch(s).should be_valid
end
@ -110,18 +110,5 @@ module Ameba
)
subject.catch(s).should be_valid
end
it "reports rule, location and message" do
s = Source.new %(
if ()
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:1:4"
issue.end_location.to_s.should eq "source.cr:1:5"
issue.message.should eq "Avoid empty expressions"
end
end
end

View file

@ -64,21 +64,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
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

View file

@ -32,17 +32,5 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated keys in hash literal: "key1", "key2"
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %q(
h = {"a" => 1, "a" => 2}
), "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:1:5"
issue.end_location.to_s.should eq "source.cr:1:24"
issue.message.should eq %(Duplicated keys in hash literal: "a")
end
end
end

View file

@ -58,18 +58,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
"foo" == "foo"
), "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:14"
issue.message.should eq "Comparison always evaluates to true"
end
end
end

View file

@ -58,18 +58,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).index(1).not_nil!
), "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:1:8"
issue.end_location.to_s.should eq "source.cr:1:24"
issue.message.should eq "Use `index! {...}` instead of `index {...}.not_nil!`"
end
end
end

View file

@ -32,18 +32,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).first?.not_nil!
), "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:1:15"
issue.end_location.to_s.should eq "source.cr:1:22"
issue.message.should eq "Avoid using `not_nil!`"
end
end
end

View file

@ -68,14 +68,14 @@ module Ameba::Rule::Lint
end
context "properties" do
it "allows to configure string_array_unwanted_symbols" do
it "#string_array_unwanted_symbols" do
rule = PercentArrays.new
rule.string_array_unwanted_symbols = ","
s = Source.new %( %w("one") )
rule.catch(s).should be_valid
end
it "allows to configure symbol_array_unwanted_symbols" do
it "#symbol_array_unwanted_symbols" do
rule = PercentArrays.new
rule.symbol_array_unwanted_symbols = ","
s = Source.new %( %i(:one) )

View file

@ -25,16 +25,5 @@ module Ameba::Rule::Lint
# ^^^^^ error: rand(1) always returns 0
CRYSTAL
end
it "reports rule, location and a message" do
s = Source.new "rand(1)", "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should eq "rand(1) always returns 0"
end
end
end

View file

@ -157,20 +157,6 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports rule, location and message" do
source = Source.new %(
foo = 1
3.times { |foo| foo + 1 }
), "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:12"
issue.end_location.should be_nil
issue.message.should eq "Shadowing outer local variable `foo`"
end
context "macro" do
it "does not report shadowed vars in outer scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -194,24 +194,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
i = 0
while true
i += 1
spawn { i }
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:4:11"
issue.end_location.to_s.should eq "source.cr:4:11"
issue.message.should eq "Shared variable `i` is used in fiber"
end
end
end

View file

@ -23,16 +23,6 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new "def hello 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:1:11"
issue.message.should match /unexpected token: "?end"? \(expected ["'];["'] or newline\)/
end
it "has highest severity" do
subject.severity.should eq Severity::Error
end

View file

@ -24,22 +24,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
case
when String
puts "hello"
when can_generate?
generate if can_generate?
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:5:15"
issue.end_location.to_s.should eq "source.cr:5:27"
issue.message.should eq "Useless condition in when detected"
end
end
end

View file

@ -46,8 +46,8 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure object_call_names" do
rule = Rule::Performance::AnyAfterFilter.new
it "#filter_names" do
rule = AnyAfterFilter.new
rule.filter_names = %w(select)
expect_no_issues rule, <<-CRYSTAL
@ -66,14 +66,5 @@ module Ameba::Rule::Performance
expect_no_corrections source
end
end
it "reports rule, pos and message" do
source = expect_issue subject, <<-CRYSTAL
[1, 2, 3].reject { |e| e > 2 }.any?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?`
CRYSTAL
expect_no_corrections source
end
end
end

View file

@ -42,18 +42,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %(
[1, 2, 3].any?
)
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.end_location.to_s.should eq "source.cr:1:14"
issue.message.should eq "Use `!{...}.empty?` instead of `{...}.any?`"
end
end
end

View file

@ -44,7 +44,7 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure `call_names`" do
it "#call_names" do
rule = ChainedCallWithNoBang.new
rule.call_names = %w(uniq)
@ -54,22 +54,6 @@ module Ameba::Rule::Performance
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: <<-CODE
[1, 2, 3].select { |e| e > 1 }.reverse
CODE
subject.catch(source).should_not be_valid
source.issues.size.should eq 1
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:32"
issue.end_location.to_s.should eq "source.cr:1:38"
issue.message.should eq "Use bang method variant `reverse!` after chained `select` call"
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -36,18 +36,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).map(&.itself).compact
), "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:1:8"
issue.end_location.to_s.should eq "source.cr:1:29"
issue.message.should eq "Use `compact_map {...}` instead of `map {...}.compact`"
end
end
end

View file

@ -62,8 +62,8 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure object_call_names" do
rule = Rule::Performance::FirstLastAfterFilter.new
it "#filter_names" do
rule = FirstLastAfterFilter.new
rule.filter_names = %w(reject)
expect_no_issues rule, <<-CRYSTAL
@ -72,21 +72,6 @@ module Ameba::Rule::Performance
end
end
it "reports rule, pos and message" do
s = Source.new %(
[1, 2, 3].select { |e| e > 2 }.first
), "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:1:11"
issue.end_location.to_s.should eq "source.cr:1:37"
issue.message.should eq "Use `find {...}` instead of `select {...}.first`"
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -30,18 +30,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
%w[Alice Bob].map(&.chars).flatten
), "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:1:15"
issue.end_location.to_s.should eq "source.cr:1:35"
issue.message.should eq "Use `flat_map {...}` instead of `map {...}.flatten`"
end
end
end

View file

@ -45,18 +45,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).map(&.to_u64).sum
), "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:1:8"
issue.end_location.to_s.should eq "source.cr:1:25"
issue.message.should eq "Use `sum {...}` instead of `map {...}.sum`"
end
end
end

View file

@ -44,8 +44,8 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure object caller names" do
rule = Rule::Performance::SizeAfterFilter.new
it "#filter_names" do
rule = SizeAfterFilter.new
rule.filter_names = %w(select)
expect_no_issues rule, <<-CRYSTAL
@ -61,18 +61,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
lines.split("\n").reject(&.empty?).size
), "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:4"
issue.end_location.to_s.should eq "source.cr:2:25"
issue.message.should eq "Use `count {...}` instead of `reject {...}.size`."
end
end
end

View file

@ -3,10 +3,10 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::ConstantNames.new
private def it_reports_constant(name, value, expected)
it "reports constant name #{expected}" do
private def it_reports_constant(name, value, expected, *, file = __FILE__, line = __LINE__)
it "reports constant name #{expected}", file, line do
rule = Rule::Style::ConstantNames.new
expect_issue rule, <<-CRYSTAL, name: name
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
%{name} = #{value}
# ^{name} error: Constant name should be screaming-cased: #{expected}, not #{name}
CRYSTAL
@ -17,7 +17,7 @@ module Ameba
it "passes if type names are screaming-cased" do
expect_no_issues subject, <<-CRYSTAL
LUCKY_NUMBERS = [3, 7, 11]
DOCUMENTATION_URL = "http://crystal-lang.org/docs"
DOCUMENTATION_URL = "https://crystal-lang.org/docs"
Int32
@ -37,19 +37,5 @@ module Ameba
# it_reports_constant "MyBadConstant", "1", "MYBADCONSTANT"
it_reports_constant "Wrong_NAME", "2", "WRONG_NAME"
it_reports_constant "Wrong_Name", "3", "WRONG_NAME"
it "reports rule, pos and message" do
s = Source.new %(
Const_Name = 1
), "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:10"
issue.message.should eq(
"Constant name should be screaming-cased: CONST_NAME, not Const_Name"
)
end
end
end

View file

@ -3,11 +3,11 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::GuardClause.new
def it_reports_body(body, *, line = __LINE__)
private def it_reports_body(body, *, file = __FILE__, line = __LINE__)
rule = Rule::Style::GuardClause.new
it "reports an issue if method body is if / unless without else" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if method body is if / unless without else", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`return unless something`) instead of wrapping the code inside a conditional expression.
@ -23,7 +23,7 @@ module Ameba
end
CRYSTAL
expect_correction source, <<-CRYSTAL, line: line
expect_correction source, <<-CRYSTAL, file: file, line: line
def func
return unless something
#{body}
@ -38,8 +38,8 @@ module Ameba
CRYSTAL
end
it "reports an issue if method body ends with if / unless without else" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if method body ends with if / unless without else", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
test
if something
@ -57,7 +57,7 @@ module Ameba
end
CRYSTAL
expect_correction source, <<-CRYSTAL, line: line
expect_correction source, <<-CRYSTAL, file: file, line: line
def func
test
return unless something
@ -75,11 +75,11 @@ module Ameba
end
end
def it_reports_control_expression(kw, *, line = __LINE__)
private def it_reports_control_expression(kw, *, file = __FILE__, line = __LINE__)
rule = Rule::Style::GuardClause.new
it "reports an issue with #{kw} in the if branch" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue with #{kw} in the if branch", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression.
@ -90,11 +90,11 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
it "reports an issue with #{kw} in the else branch" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue with #{kw} in the else branch", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} unless something`) instead of wrapping the code inside a conditional expression.
@ -105,11 +105,11 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
it "doesn't report an issue if condition has multiple lines" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "doesn't report an issue if condition has multiple lines", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something &&
something_else
@ -121,8 +121,8 @@ module Ameba
CRYSTAL
end
it "does not report an issue if #{kw} is inside elsif" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "does not report an issue if #{kw} is inside elsif", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
a
@ -133,8 +133,8 @@ module Ameba
CRYSTAL
end
it "does not report an issue if #{kw} is inside if..elsif..else..end" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "does not report an issue if #{kw} is inside if..elsif..else..end", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
a
@ -147,8 +147,8 @@ module Ameba
CRYSTAL
end
it "doesn't report an issue if control flow expr has multiple lines" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "doesn't report an issue if control flow expr has multiple lines", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
#{kw} \\
@ -161,8 +161,8 @@ module Ameba
CRYSTAL
end
it "reports an issue if non-control-flow branch has multiple lines" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if non-control-flow branch has multiple lines", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression.
@ -174,7 +174,7 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
end

View file

@ -42,9 +42,10 @@ module Ameba::Rule::Style
end
context "properties" do
it "allows to configure filter_names" do
it "#filter_names" do
rule = IsAFilter.new
rule.filter_names = %w(select)
expect_no_issues rule, <<-CRYSTAL
[1, 2, nil].reject(&.nil?)
CRYSTAL
@ -58,20 +59,5 @@ module Ameba::Rule::Style
CRYSTAL
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %(
[1, 2, nil].reject(&.nil?)
)
subject.catch(source).should_not be_valid
source.issues.size.should eq 1
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:13"
issue.end_location.to_s.should eq "source.cr:1:26"
issue.message.should eq "Use `reject(Nil)` instead of `reject {...}`"
end
end
end

View file

@ -34,19 +34,5 @@ module Ameba::Rule::Style
a.nil?
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
nil.is_a? Nil
), "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:1:11"
issue.end_location.to_s.should eq "source.cr:1:13"
issue.message.should eq IsANil::MSG
end
end
end

View file

@ -3,12 +3,12 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::LargeNumbers.new
private def it_transforms(number, expected)
it "transforms large number #{number}" do
private def it_transforms(number, expected, *, file = __FILE__, line = __LINE__)
it "transforms large number #{number}", file, line do
rule = Rule::Style::LargeNumbers.new
rule.int_min_digits = 5
source = expect_issue rule, <<-CRYSTAL, number: number
source = expect_issue rule, <<-CRYSTAL, number: number, file: file, line: line
number = %{number}
# ^{number} error: Large numbers should be written with underscores: #{expected}
CRYSTAL
@ -97,10 +97,12 @@ module Ameba
it_transforms "10000_i16", "10_000_i16"
it_transforms "10000_i32", "10_000_i32"
it_transforms "10000_i64", "10_000_i64"
it_transforms "10000_i128", "10_000_i128"
it_transforms "10000_u16", "10_000_u16"
it_transforms "10000_u32", "10_000_u32"
it_transforms "10000_u64", "10_000_u64"
it_transforms "10000_u128", "10_000_u128"
it_transforms "123456_f32", "123_456_f32"
it_transforms "123456_f64", "123_456_f64"
@ -117,20 +119,8 @@ module Ameba
it_transforms "3.001234", "3.001_234"
it_transforms "3.0012345", "3.001_234_5"
it "reports rule, pos and message" do
s = Source.new %q(
1200000
), "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should match /1_200_000/
end
context "properties" do
it "allows to configure integer min digits" do
it "#int_min_digits" do
rule = Rule::Style::LargeNumbers.new
rule.int_min_digits = 10
expect_no_issues rule, %q(1200000)

View file

@ -3,10 +3,10 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::MethodNames.new
private def it_reports_method_name(name, expected)
it "reports method name #{expected}" do
private def it_reports_method_name(name, expected, *, file = __FILE__, line = __LINE__)
it "reports method name #{expected}", file, line do
rule = Rule::Style::MethodNames.new
expect_issue rule, <<-CRYSTAL, name: name
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
def %{name}; end
# ^{name} error: Method name should be underscore-cased: #{expected}, not %{name}
CRYSTAL
@ -38,20 +38,5 @@ module Ameba
it_reports_method_name "firstName", "first_name"
it_reports_method_name "date_of_Birth", "date_of_birth"
it_reports_method_name "homepageURL", "homepage_url"
it "reports rule, pos and message" do
s = Source.new %(
def bad_Name(a)
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:1:5"
issue.end_location.to_s.should eq "source.cr:1:12"
issue.message.should eq(
"Method name should be underscore-cased: bad_name, not bad_Name"
)
end
end
end

View file

@ -53,16 +53,5 @@ module Ameba::Rule::Style
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new ":nok unless !s.empty?", "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:21"
issue.message.should eq "Avoid negated conditions in unless blocks"
end
end
end

View file

@ -43,7 +43,7 @@ module Ameba::Rule::Style
end
context "properties" do
context "#exclude_ternary=" do
context "#exclude_ternary" do
it "skips ternary control expressions by default" do
expect_no_issues subject, <<-CRYSTAL
(foo > bar) ? true : false
@ -51,7 +51,7 @@ module Ameba::Rule::Style
end
it "allows to configure assignments" do
rule = Rule::Style::ParenthesesAroundCondition.new
rule = ParenthesesAroundCondition.new
rule.exclude_ternary = false
expect_issue rule, <<-CRYSTAL
@ -75,7 +75,7 @@ module Ameba::Rule::Style
end
end
context "#allow_safe_assignment=" do
context "#allow_safe_assignment" do
it "reports assignments by default" do
expect_issue subject, <<-CRYSTAL
if (foo = @foo)
@ -98,7 +98,7 @@ module Ameba::Rule::Style
end
it "allows to configure assignments" do
rule = Rule::Style::ParenthesesAroundCondition.new
rule = ParenthesesAroundCondition.new
rule.allow_safe_assignment = true
expect_issue rule, <<-CRYSTAL

View file

@ -27,24 +27,6 @@ module Ameba::Rule::Style
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %q(
class Image
def is_valid?(x)
true
end
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.end_location.to_s.should eq "source.cr:4:5"
issue.message.should eq(
"Favour method name 'valid?' over 'is_valid?'")
end
it "ignores if alternative name isn't valid syntax" do
expect_no_issues subject, <<-CRYSTAL
class Image

View file

@ -294,24 +294,5 @@ module Ameba::Rule::Style
}
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %q(
def method
begin
open_connection
ensure
close_connection
end
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.end_location.to_s.should eq "source.cr:2:7"
issue.message.should eq "Redundant `begin` block detected"
end
end
end

View file

@ -201,7 +201,7 @@ module Ameba::Rule::Style
end
context "properties" do
context "#allow_multi_next=" do
context "#allow_multi_next" do
it "allows multi next statements by default" do
expect_no_issues subject, <<-CRYSTAL
block do |a, b|
@ -211,7 +211,7 @@ module Ameba::Rule::Style
end
it "allows to configure multi next statements" do
rule = Rule::Style::RedundantNext.new
rule = RedundantNext.new
rule.allow_multi_next = false
source = expect_issue rule, <<-CRYSTAL
block do |a, b|
@ -238,7 +238,7 @@ module Ameba::Rule::Style
end
it "allows to configure empty next statements" do
rule = Rule::Style::RedundantNext.new
rule = RedundantNext.new
rule.allow_empty_next = false
source = expect_issue rule, <<-CRYSTAL
block do

View file

@ -284,7 +284,7 @@ module Ameba::Rule::Style
end
context "properties" do
context "#allow_multi_return=" do
context "#allow_multi_return" do
it "allows multi returns by default" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b)
@ -294,7 +294,7 @@ module Ameba::Rule::Style
end
it "allows to configure multi returns" do
rule = Rule::Style::RedundantReturn.new
rule = RedundantReturn.new
rule.allow_multi_return = false
source = expect_issue rule, <<-CRYSTAL
def method(a, b)
@ -321,7 +321,7 @@ module Ameba::Rule::Style
end
it "allows to configure empty returns" do
rule = Rule::Style::RedundantReturn.new
rule = RedundantReturn.new
rule.allow_empty_return = false
source = expect_issue rule, <<-CRYSTAL
def method

View file

@ -3,10 +3,10 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::TypeNames.new
private def it_reports_name(type, name, expected)
it "reports type name #{expected}" do
private def it_reports_name(type, name, expected, *, file = __FILE__, line = __LINE__)
it "reports type name #{expected}", file, line do
rule = Rule::Style::TypeNames.new
expect_issue rule, <<-CRYSTAL, type: type, name: name
expect_issue rule, <<-CRYSTAL, type: type, name: name, file: file, line: line
%{type} %{name}; end
# ^{type}^{name}^^^^ error: Type name should be camelcased: #{expected}, but it was %{name}
CRYSTAL
@ -49,20 +49,5 @@ module Ameba
# ^{} error: Type name should be camelcased: NumericValue, but it was Numeric_value
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %(
class My_class
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:1:1"
issue.end_location.to_s.should eq "source.cr:2:3"
issue.message.should eq(
"Type name should be camelcased: MyClass, but it was My_class"
)
end
end
end

View file

@ -22,23 +22,5 @@ module Ameba::Rule::Style
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %(
unless something
:one
else
:two
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.should_not be_nil
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:5:3"
issue.message.should eq "Favour if over unless with else"
end
end
end

View file

@ -3,10 +3,10 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::VariableNames.new
private def it_reports_var_name(name, value, expected)
it "reports variable name #{expected}" do
private def it_reports_var_name(name, value, expected, *, file = __FILE__, line = __LINE__)
it "reports variable name #{expected}", file, line do
rule = Rule::Style::VariableNames.new
expect_issue rule, <<-CRYSTAL, name: name
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
%{name} = #{value}
# ^{name} error: Var name should be underscore-cased: #{expected}, not %{name}
CRYSTAL
@ -62,19 +62,5 @@ module Ameba
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %(
badName = "Yeah"
), "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:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should eq(
"Var name should be underscore-cased: bad_name, not badName"
)
end
end
end

View file

@ -64,10 +64,12 @@ module Ameba::Rule::Style
context "properties" do
it "#exclude_calls_with_block" do
rule = VerboseBlock.new
rule.exclude_calls_with_block = true
expect_no_issues rule, <<-CRYSTAL
(1..3).in_groups_of(1) { |i| i.map(&.to_s) }
CRYSTAL
rule.exclude_calls_with_block = false
source = expect_issue rule, <<-CRYSTAL
(1..3).in_groups_of(1) { |i| i.map(&.to_s) }
@ -81,12 +83,14 @@ module Ameba::Rule::Style
it "#exclude_multiple_line_blocks" do
rule = VerboseBlock.new
rule.exclude_multiple_line_blocks = true
expect_no_issues rule, <<-CRYSTAL
(1..3).any? do |i|
i.odd?
end
CRYSTAL
rule.exclude_multiple_line_blocks = false
source = expect_issue rule, <<-CRYSTAL
(1..3).any? do |i|
@ -102,12 +106,14 @@ module Ameba::Rule::Style
it "#exclude_prefix_operators" do
rule = VerboseBlock.new
rule.exclude_prefix_operators = true
expect_no_issues rule, <<-CRYSTAL
(1..3).sum { |i| +i }
(1..3).sum { |i| -i }
(1..3).sum { |i| ~i }
CRYSTAL
rule.exclude_prefix_operators = false
rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL
@ -128,10 +134,12 @@ module Ameba::Rule::Style
it "#exclude_operators" do
rule = VerboseBlock.new
rule.exclude_operators = true
expect_no_issues rule, <<-CRYSTAL
(1..3).sum { |i| i * 2 }
CRYSTAL
rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL
(1..3).sum { |i| i * 2 }
@ -145,10 +153,12 @@ module Ameba::Rule::Style
it "#exclude_setters" do
rule = VerboseBlock.new
rule.exclude_setters = true
expect_no_issues rule, <<-CRYSTAL
Char::Reader.new("abc").tap { |reader| reader.pos = 0 }
CRYSTAL
rule.exclude_setters = false
source = expect_issue rule, <<-CRYSTAL
Char::Reader.new("abc").tap { |reader| reader.pos = 0 }
@ -163,12 +173,14 @@ module Ameba::Rule::Style
it "#max_line_length" do
rule = VerboseBlock.new
rule.exclude_multiple_line_blocks = false
rule.max_line_length = 60
expect_no_issues rule, <<-CRYSTAL
(1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i|
i.to_s.reverse.strip.blank?
end
CRYSTAL
rule.max_line_length = nil
source = expect_issue rule, <<-CRYSTAL
(1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i|
@ -184,10 +196,12 @@ module Ameba::Rule::Style
it "#max_length" do
rule = VerboseBlock.new
rule.max_length = 30
expect_no_issues rule, <<-CRYSTAL
(1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? }
CRYSTAL
rule.max_length = nil
source = expect_issue rule, <<-CRYSTAL
(1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? }
@ -216,6 +230,7 @@ module Ameba::Rule::Style
it "reports call args and named_args" do
rule = VerboseBlock.new
rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL
(1..3).map { |i| i.to_s[start: 0.to_i64, count: 3]? }
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: [...] `map(&.to_s.[start: 0.to_i64, count: 3]?)`
@ -258,20 +273,5 @@ module Ameba::Rule::Style
(1..3).join(separator: '.', &.to_s)
CRYSTAL
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %(
(1..3).any? { |i| i.odd? }
)
subject.catch(source).should_not be_valid
source.issues.size.should eq 1
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:8"
issue.end_location.to_s.should eq "source.cr:1:26"
issue.message.should eq "Use short block notation instead: `any?(&.odd?)`"
end
end
end

View file

@ -65,6 +65,7 @@ module Ameba
rules = [AtoAA.new] of Rule::Base
source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoAA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end
@ -95,11 +96,11 @@ module Ameba
it "does not run other rules" do
rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base
source = Source.new %q(
MyBadConstant = 1
source = Source.new <<-CRYSTAL
MyBadConstant = 1
when my_bad_syntax
)
when my_bad_syntax
CRYSTAL
Runner.new(rules, [source], formatter, default_severity).run
source.should_not be_valid
@ -110,9 +111,9 @@ module Ameba
context "unneeded disables" do
it "reports an issue if such disable exists" do
rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base
source = Source.new %(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable LineLength
)
CRYSTAL
Runner.new(rules, [source], formatter, default_severity).run
source.should_not be_valid
@ -134,9 +135,7 @@ module Ameba
it "writes the explanation if sources are not valid and location found" do
io.clear
rules = [ErrorRule.new] of Rule::Base
source = Source.new %(
a = 1
), "source.cr"
source = Source.new "a = 1", "source.cr"
runner = Runner.new(rules, [source], formatter, default_severity).run
runner.explain({file: "source.cr", line: 1, column: 1}, io)
@ -146,9 +145,7 @@ module Ameba
it "writes nothing if sources are not valid and location is not found" do
io.clear
rules = [ErrorRule.new] of Rule::Base
source = Source.new %(
a = 1
), "source.cr"
source = Source.new "a = 1", "source.cr"
runner = Runner.new(rules, [source], formatter, default_severity).run
runner.explain({file: "source.cr", line: 1, column: 2}, io)
@ -167,10 +164,9 @@ module Ameba
it "returns false if there are invalid sources" do
rules = Rule.rules.map &.new.as(Rule::Base)
s = Source.new %q(
WrongConstant = 5
)
Runner.new(rules, [s], formatter, default_severity).run.success?.should be_false
source = Source.new "WrongConstant = 5"
Runner.new(rules, [source], formatter, default_severity).run.success?.should be_false
end
it "depends on the level of severity" do
@ -184,11 +180,11 @@ module Ameba
it "returns false if issue is disabled" do
rules = [NamedRule.new] of Rule::Base
source = Source.new %(
source = Source.new <<-CRYSTAL
def foo
bar = 1 # ameba:disable #{NamedRule.name}
end
)
CRYSTAL
source.add_issue NamedRule.new, location: {2, 1},
message: "Useless assignment"
@ -205,6 +201,7 @@ module Ameba
rules = [AtoB.new, BtoA.new]
source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end
@ -214,11 +211,12 @@ module Ameba
context "if there are multiple offenses in an inspected file" do
it "aborts because of an infinite loop" do
rules = [AtoB.new, BtoA.new]
source = Source.new %(
source = Source.new <<-CRYSTAL, "source.cr"
class A; end
class A_A; end
), "source.cr"
CRYSTAL
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end
@ -231,6 +229,7 @@ module Ameba
rules = [ClassToModule.new, ModuleToClass.new, AtoB.new, BtoA.new]
source = Source.new "class A_A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/ClassToModule, Ameba/AtoB -> Ameba/ModuleToClass, Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end
@ -242,6 +241,7 @@ module Ameba
rules = [AtoB.new, BtoC.new, CtoA.new]
source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoC -> Ameba/CtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end

View file

@ -1,13 +1,13 @@
require "../spec_helper"
module Ameba
private def it_tokenizes(str, expected)
it "tokenizes #{str}" do
([] of String).tap do |token_types|
private def it_tokenizes(str, expected, *, file = __FILE__, line = __LINE__)
it "tokenizes #{str}", file, line do
%w[].tap do |token_types|
Tokenizer.new(Source.new str, normalize: false)
.run { |token| token_types << token.type.to_s }
.should be_true
end.should eq expected
end.should eq(expected), file: file, line: line
end
end

View file

@ -286,3 +286,7 @@ end
def as_nodes(source)
Ameba::TestNodeVisitor.new(as_node source)
end
def trailing_whitespace
' '
end

View file

@ -1,15 +1,21 @@
module Ameba::Rule::Lint
# This rule checks for mistyped shorthand assignments.
#
# # bad
# x =- y
# x =+ y
# x =! y
# This is considered invalid:
#
# # good
# x -= y # or x = -y
# x += y # or x = +y
# x != y # or x = !y
# ```
# x = -y
# x = +y
# x = !y
# ```
#
# And this is valid:
#
# ```
# x -= y # or x = -y
# x += y # or x = +y
# x != y # or x = !y
# ```
#
# YAML configuration example:
#
@ -27,9 +33,9 @@ module Ameba::Rule::Lint
MSG = "Suspicious assignment detected. Did you mean `%s`?"
MISTAKES = {
"=-" => "-=",
"=+" => "+=",
"=!" => "!=",
"=-": "-=",
"=+": "+=",
"=!": "!=",
}
def test(source, node : Crystal::Assign)
@ -43,9 +49,9 @@ module Ameba::Rule::Lint
op_text = source_between(op_location, op_end_location, source.lines)
return unless op_text
return unless MISTAKES.has_key?(op_text)
return unless suggestion = MISTAKES[op_text]?
issue_for op_location, op_end_location, MSG % MISTAKES[op_text]
issue_for op_location, op_end_location, MSG % suggestion
end
end
end

View file

@ -57,8 +57,7 @@ module Ameba::Rule::Style
if cond.is_a?(Crystal::Assign) && allow_safe_assignment?
issue_for cond, MSG_MISSING do |corrector|
corrector.insert_before(cond, '(')
corrector.insert_after(cond, ')')
corrector.wrap(cond, '(', ')')
end
return
end

View file

@ -130,7 +130,7 @@ module Ameba
rule.test(source)
end
check_unneeded_directives(source)
break unless autocorrect? && source.correct
break unless autocorrect? && source.correct?
# The issues that couldn't be corrected will be found again so we
# only keep the corrected ones in order to avoid duplicate reporting.

View file

@ -24,9 +24,10 @@ module Ameba
# Corrects any correctable issues and updates `code`.
# Returns `false` if no issues were corrected.
def correct
def correct?
corrector = Corrector.new(code)
issues.each(&.correct(corrector))
corrected_code = corrector.process
return false if code == corrected_code

View file

@ -125,7 +125,9 @@ class Ameba::Source
private def check_range_validity(begin_pos, end_pos)
return unless begin_pos < 0 || end_pos > code.size
raise IndexError.new("The range #{begin_pos}...#{end_pos} is outside the bounds of the source")
raise IndexError.new(
"The range #{begin_pos}...#{end_pos} is outside the bounds of the source"
)
end
end
end

View file

@ -2,7 +2,8 @@
class Ameba::Spec::AnnotatedSource
ANNOTATION_PATTERN_1 = /\A\s*(# )?(\^+|\^{})( error:)? /
ANNOTATION_PATTERN_2 = " # error: "
ABBREV = "[...]"
ABBREV = "[...]"
getter lines : Array(String)
@ -15,6 +16,7 @@ class Ameba::Spec::AnnotatedSource
def self.parse(annotated_code)
lines = [] of String
annotations = [] of {Int32, String, String}
code_lines = annotated_code.split('\n') # must preserve trailing newline
code_lines.each do |code_line|
case
@ -39,7 +41,9 @@ class Ameba::Spec::AnnotatedSource
# NOTE: Annotations are sorted so that reconstructing the annotation
# text via `#to_s` is deterministic.
def initialize(@lines, annotations : Enumerable({Int32, String, String}))
@annotations = annotations.to_a.sort_by { |line, _, message| {line, message} }
@annotations = annotations.to_a.sort_by do |line, _, message|
{line, message}
end
end
# Annotates the source code with the Ameba issues provided.
@ -47,7 +51,9 @@ class Ameba::Spec::AnnotatedSource
# NOTE: Annotations are sorted so that reconstructing the annotation
# text via `#to_s` is deterministic.
def initialize(@lines, issues : Enumerable(Issue))
@annotations = issues_to_annotations(issues).sort_by { |line, _, message| {line, message} }
@annotations = issues_to_annotations(issues).sort_by do |line, _, message|
{line, message}
end
end
def ==(other)

View file

@ -13,8 +13,8 @@ module Ameba::Spec
def failure_message(source)
String.build do |str|
str << "Source expected to be valid, but there are issues: \n\n"
source.issues.reject(&.disabled?).each do |e|
str << " * #{e.rule.name}: #{e.message}\n"
source.issues.reject(&.disabled?).each do |issue|
str << " * #{issue.rule.name}: #{issue.message}\n"
end
end
end

View file

@ -129,7 +129,7 @@ module Ameba::Spec::ExpectIssue
end
def expect_correction(source, correction, *, file = __FILE__, line = __LINE__)
raise "Use `expect_no_corrections` if the code will not change" unless source.correct
raise "Use `expect_no_corrections` if the code will not change" unless source.correct?
return if correction == source.code
fail <<-MSG, file, line
@ -144,7 +144,7 @@ module Ameba::Spec::ExpectIssue
end
def expect_no_corrections(source, *, file = __FILE__, line = __LINE__)
return unless source.correct
return unless source.correct?
fail <<-MSG, file, line
Expected no corrections, but got:

View file

@ -14,9 +14,5 @@ module Ameba
end
end
def trailing_whitespace
' '
end
include Ameba::Spec::BeValid
include Ameba::Spec::ExpectIssue