Skip to content

Complex conditions in Style/NegatedConditionsInUnless #621

Open

Description

@straight-shoota

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.

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

  1. Sija commented on Jun 19, 2026

    @Sija
    Member

    Any improvements to the detection logic are welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions