From 2f837375ec10914374a4d9f9d17919885ac11ebf Mon Sep 17 00:00:00 2001 From: Vitalii Elenhaupt Date: Thu, 8 Mar 2018 18:55:35 +0200 Subject: [PATCH] New rule: rand zero --- spec/ameba/rule/rand_zero_spec.cr | 36 ++++++++++++++++++++++++ src/ameba/rule/rand_zero.cr | 46 +++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 spec/ameba/rule/rand_zero_spec.cr create mode 100644 src/ameba/rule/rand_zero.cr diff --git a/spec/ameba/rule/rand_zero_spec.cr b/spec/ameba/rule/rand_zero_spec.cr new file mode 100644 index 00000000..cb886cb4 --- /dev/null +++ b/spec/ameba/rule/rand_zero_spec.cr @@ -0,0 +1,36 @@ +require "../../spec_helper" + +module Ameba::Rule + describe RandZero do + subject = RandZero.new + + it "passes if it is not rand(1) or rand(0)" do + s = Source.new %( + rand(1.0) + rand(0.11) + rand(2) + ) + subject.catch(s).should be_valid + end + + it "fails if it is rand(0)" do + s = Source.new "rand(0)" + subject.catch(s).should_not be_valid + end + + it "fails if it is rand(1)" do + s = Source.new "rand(1)" + subject.catch(s).should_not be_valid + end + + it "reports rule, location and a message" do + s = Source.new "rand(1)", "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:1:1" + error.message.should eq "rand(1) always returns 0" + end + end +end diff --git a/src/ameba/rule/rand_zero.cr b/src/ameba/rule/rand_zero.cr new file mode 100644 index 00000000..403850f5 --- /dev/null +++ b/src/ameba/rule/rand_zero.cr @@ -0,0 +1,46 @@ +module Ameba::Rule + # A rule that disallows `rand(0)` and `rand(1)` calls. + # Such calls always return `0`. + # + # For example: + # + # ``` + # rand(1) + # ``` + # + # Should be written as: + # + # ``` + # rand + # # or + # rand(2) + # ``` + # + # YAML configuration example: + # + # ``` + # RandZero: + # Enabled: true + # ``` + # + struct RandZero < Base + properties do + description = "Disallows rand zero calls" + end + + def test(source) + AST::Visitor.new self, source + end + + def test(source, node : Crystal::Call) + return unless node.name == "rand" && + node.args.size == 1 && + (arg = node.args.first) && + (arg.is_a? Crystal::NumberLiteral) && + (value = arg.value) && + (value == "0" || value == "1") + + source.error self, node.location, "#{node} always returns 0" + end + end +end