diff --git a/spec/ameba/rule/naming/binary_operator_parameter_name_spec.cr b/spec/ameba/rule/naming/binary_operator_parameter_name_spec.cr new file mode 100644 index 000000000..bd27a96cb --- /dev/null +++ b/spec/ameba/rule/naming/binary_operator_parameter_name_spec.cr @@ -0,0 +1,50 @@ +require "../../../spec_helper" + +module Ameba::Rule::Naming + subject = BinaryOperatorParameterName.new + + describe BinaryOperatorParameterName do + it "ignores `other` parameter name in binary method definitions" do + expect_no_issues subject, <<-CRYSTAL + def +(other); end + def -(other); end + def *(other); end + CRYSTAL + end + + it "ignores binary method definitions with arity other than 1" do + expect_no_issues subject, <<-CRYSTAL + def +; end + def +(foo, bar); end + def -; end + def -(foo, bar); end + CRYSTAL + end + + it "ignores non-binary method definitions" do + expect_no_issues subject, <<-CRYSTAL + def foo(bar); end + def bąk(genus); end + CRYSTAL + end + + it "reports binary methods definitions with incorrectly named parameter" do + expect_issue subject, <<-CRYSTAL + def +(foo); end + # ^ error: When defining the `+` operator, name its argument `other` + def -(foo); end + # ^ error: When defining the `-` operator, name its argument `other` + def *(foo); end + # ^ error: When defining the `*` operator, name its argument `other` + CRYSTAL + end + + it "ignores methods from #excluded_operators" do + subject.excluded_operators.each do |op| + expect_no_issues subject, <<-CRYSTAL + def #{op}(foo); end + CRYSTAL + end + end + end +end diff --git a/src/ameba/rule/naming/binary_operator_parameter_name.cr b/src/ameba/rule/naming/binary_operator_parameter_name.cr new file mode 100644 index 000000000..eb77cefc6 --- /dev/null +++ b/src/ameba/rule/naming/binary_operator_parameter_name.cr @@ -0,0 +1,50 @@ +module Ameba::Rule::Naming + # A rule that enforces that certain binary operator methods have + # their sole parameter named `other`. + # + # For example, this is considered valid: + # + # ``` + # class Money + # def +(other) + # end + # end + # ``` + # + # And this is invalid parameter name: + # + # ``` + # class Money + # def +(amount) + # end + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Naming/BinaryOperatorParameterName: + # Enabled: true + # ExcludedOperators: ["[]", "[]?", "[]=", "<<", "=~"] + # ``` + class BinaryOperatorParameterName < Base + properties do + description "Enforces that certain binary operator methods have " \ + "their sole parameter named `other`" + excluded_operators %w[[] []? []= << ` =~] + end + + MSG = "When defining the `%s` operator, name its argument `other`" + + def test(source, node : Crystal::Def) + name = node.name + + return if name == "->" || name.in?(excluded_operators) + return if name.chars.any?(&.alphanumeric?) + return unless node.args.size == 1 + return if (arg = node.args.first).name == "other" + + issue_for arg, MSG % name + end + end +end