Repository navigation
Complex conditions in Style/NegatedConditionsInUnless #621
Description
Avoiding double negation is good because it reduces cognitive load.
But apparently Style/NegatedConditionsInUnless considers any negation inside an unless condition a violation, even if the conditional is a complex expression like a && (b || !c).
This seems to be expected behaviour per spec.
ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr
Lines 48 to 56 in d861f4e
| it "fails if one of inner conditions is negated" do | |
| expect_issue subject, <<-CRYSTAL | |
| unless a && (b || !c) | |
| # ^^^^^^^^^^^^^^^^^^^ error: Avoid negated conditions in unless blocks | |
| :nok | |
| end | |
| CRYSTAL | |
| end | |
| end |
But this feels too strict and I don't follow the reasoning.
Rewriting a complex expression just to avoid negations in unless can produce more cognitive issues than existed in the first place.
Some examples for violations found in https://github.com/crystal-lang/crystal/tree/31a9906422b8bb9f17415c22fb4eacc9f2f93ed8
I would consider most of them are fine. Refactoring them would unlikely yield much improvement (if at all).
# src/steppable.cr:57:9
break unless sign == direction || (sign == 0 && !exclusive)
# src/html.cr:209:7
unless codepoint == 0x007F ||
# unicode noncharacters
(0xFDD0..0xFDEF).includes?(codepoint) ||
# last two of each plane (nonchars) disallowed
codepoint & 0xFFFF >= 0xFFFE ||
# unicode control characters except space
(codepoint < 0x0020 && !codepoint.in?(0x0009, 0x000A, 0x000C))
# src/path.cr:766:7
unless base.absolute? || !expand_base
# src/crystal/system/unix/syscall.cr:1:4
{% skip_file unless flag?(:linux) && !flag?(:interpreted) %}
# src/compiler/crystal/command/repl.cr:52:5
return unless STDERR.tty? && !ENV.has_key?("CRYSTAL_INTERPRETER_SKIP_BANNER")
# src/compiler/crystal/semantic/main_visitor.cr:2199:11
unless break_var && !break_var.same?(while_var)
# src/compiler/crystal/semantic/type_declaration_visitor.cr:61:5
unless included_type.is_a?(NonGenericClassType) && included_type.extern? && !included_type.extern_union?
# src/compiler/crystal/types.cr:2657:7
unless t.is_a?(TypeParameter) || (t.class? && !t.struct?)
Activity
Sija commentedon Jun 19, 2026
Any improvements to the detection logic are welcome!