diff --git a/spec/ameba/rule/lint/literal_assignments_in_expressions_spec.cr b/spec/ameba/rule/lint/literal_assignments_in_expressions_spec.cr new file mode 100644 index 000000000..2902a5f17 --- /dev/null +++ b/spec/ameba/rule/lint/literal_assignments_in_expressions_spec.cr @@ -0,0 +1,47 @@ +require "../../../spec_helper" + +LITERAL_SAMPLES = { + nil, true, 42, 4.2, 'c', "foo", :foo, /foo/, + 0..42, [1, 2, 3], {1, 2, 3}, + {foo: :bar}, {:foo => :bar}, +} + +module Ameba::Rule::Lint + subject = LiteralAssignmentsInExpressions.new + + describe LiteralAssignmentsInExpressions do + it "passes if the assignment value is not a literal" do + expect_no_issues subject, <<-CRYSTAL + if a = b + :ok + end + + unless a = b.presence + :ok + end + + :ok if a = b + :ok unless a = b + + case {a, b} + when {0, 1} then :gt + when {1, 0} then :lt + end + CRYSTAL + end + + {% for literal in LITERAL_SAMPLES %} + it %(reports if the assignment value is a {{ literal }} literal) do + expect_issue subject, <<-CRYSTAL, literal: {{ literal.stringify }} + raise "boo!" if foo = {{ literal }} + # ^{literal}^^^^^^ error: Detected assignment with a literal value in control expression + CRYSTAL + + expect_issue subject, <<-CRYSTAL, literal: {{ literal.stringify }} + raise "boo!" unless foo = {{ literal }} + # ^{literal}^^^^^^ error: Detected assignment with a literal value in control expression + CRYSTAL + end + {% end %} + end +end diff --git a/src/ameba/rule/lint/literal_assignments_in_expressions.cr b/src/ameba/rule/lint/literal_assignments_in_expressions.cr new file mode 100644 index 000000000..3cc658181 --- /dev/null +++ b/src/ameba/rule/lint/literal_assignments_in_expressions.cr @@ -0,0 +1,43 @@ +module Ameba::Rule::Lint + # A rule that disallows assignments with literal values + # in control expressions. + # + # For example, this is considered invalid: + # + # ``` + # if foo = 42 + # do_something + # end + # ``` + # + # And most likely should be replaced by the following: + # + # ``` + # if foo == 42 + # do_something + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/LiteralAssignmentsInExpressions: + # Enabled: true + # ``` + class LiteralAssignmentsInExpressions < Base + include AST::Util + + properties do + description "Disallows assignments with literal values in control expressions" + end + + MSG = "Detected assignment with a literal value in control expression" + + def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case | Crystal::While | Crystal::Until) + return unless (cond = node.cond).is_a?(Crystal::Assign) + return unless literal?(cond.value, include_paths: true) + + issue_for cond, MSG + end + end +end