New rule: hash duplicated key

This commit is contained in:
Vitalii Elenhaupt 2017-11-18 00:43:19 +02:00
parent a947dee851
commit 1839ef2ab5
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
4 changed files with 86 additions and 0 deletions

View file

@ -18,6 +18,10 @@ EmptyExpression:
# Disallows empty expressions. # Disallows empty expressions.
Enabled: true Enabled: true
HashDuplicatedKey:
# Disallows duplicated keys in hash literals.
Enabled: true
LargeNumbers: LargeNumbers:
# A rule that disallows usage of large numbers without underscore. # A rule that disallows usage of large numbers without underscore.
Enabled: true Enabled: true

View file

@ -0,0 +1,41 @@
require "../../spec_helper"
module Ameba::Rule
describe HashDuplicatedKey do
subject = HashDuplicatedKey.new
it "passes if there is no duplicated keys in a hash literals" do
s = Source.new %(
h = {"a" => 1, :a => 2, "b" => 3}
h = {"a" => 1, "b" => 2, "c" => {"a" => 3, "b" => 4}}
h = {} of String => String
)
subject.catch(s).should be_valid
end
it "fails if there is a duplicated key in a hash literal" do
s = Source.new %q(
h = {"a" => 1, "b" => 2, "a" => 3}
)
subject.catch(s).should_not be_valid
end
it "fails if there is a duplicated key in the inner hash literal" do
s = Source.new %q(
h = {"a" => 1, "b" => {"a" => 3, "b" => 4, "a" => 5}}
)
subject.catch(s).should_not be_valid
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
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:13"
error.message.should eq "Duplicated keys in hash literal."
end
end
end

View file

@ -14,6 +14,7 @@ module Ameba::AST
EnumDef, EnumDef,
ExceptionHandler, ExceptionHandler,
Expressions, Expressions,
HashLiteral,
If, If,
InstanceVar, InstanceVar,
LibDef, LibDef,

View file

@ -0,0 +1,40 @@
module Ameba::Rule
# A rule that disallows duplicated keys in hash literals.
#
# This is considered invalid:
#
# ```
# h = {"foo" => 1, "bar" => 2, "foo" => 3}
# ```
#
# And it has to written as this instead:
#
# ```
# h = {"foo" => 1, "bar" => 2}
# ```
#
# YAML configuration example:
#
# ```
# HashDuplicatedKey:
# Enabled: true
# ```
#
struct HashDuplicatedKey < Base
def test(source)
AST::Visitor.new self, source
end
def test(source, node : Crystal::HashLiteral)
return unless duplicated_keys?(node.entries)
source.error self, node.location, "Duplicated keys in hash literal."
end
private def duplicated_keys?(entries)
entries.map(&.key)
.group_by(&.itself)
.any? { |_, v| v.size > 1 }
end
end
end