Skip to content

HasMinimumLines/HasMaximumLines/HasExactLines don't count a lone \r or Unicode line separator, contradicting IsMultiLine/IsSingleLine: "a\rb" is multi-line but has "1 line" #299

Closed

Description

@matt-edmondson

What's wrong

The five line-related validation attributes in Semantics.Strings/Validation/Attributes/Format/ disagree on what a line break is.

  • IsSingleLineAttribute and IsMultiLineAttribute treat '\n', '\r' and any UnicodeCategory.LineSeparator character as a line break.
  • HasMinimumLinesAttribute, HasMaximumLinesAttribute and HasExactLinesAttribute count lines as value.Count(c => c == '\n') + 1.

The \r\n "don't double count" block in those three changes nothing. It computes crlfCount + (lfTotal - crlfCount) + 1, which equals the original lfTotal + 1.

Failure scenario

For "a\rb", and for "a b":

Attribute Result
[IsMultiLine] valid
[IsSingleLine] invalid
[HasMinimumLines(2)] invalid (counts 1 line)
[HasMaximumLines(1)] valid
[HasExactLines(2)] invalid

Why it matters

  • These attributes are meant to be combined. [HasMaximumLines(1)] lets through strings that [IsSingleLine] rejects.
  • [IsMultiLine, HasMinimumLines(2)] rejects a string that IsMultiLine just accepted.
  • Classic Mac line endings, and text containing U+2028, bypass line-count limits entirely.

Suggested fix

Add one shared helper, for example internal static int CountLines(string value), and use it in all three Has*Lines attributes. It should treat \r\n as one break, and a lone \r, \n, U+2028 or U+2029 as one break each. Remove the no-op CRLF block.

Acceptance criteria

  • "a\rb", "a\nb", "a\r\nb" and "a b" each count as 2 lines in HasMinimumLines, HasMaximumLines and HasExactLines.
  • "a\r\n\r\nb" counts as 3.
  • Tests cover each line ending.

Activity

  1. matt-edmondson commented on Sep 28, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: Medium. It is a validation inconsistency: combined attributes contradict each other, and a lone \r or U+2028 slips past line-count limits. The failure is a wrong accept or reject, not a crash.
    • Area / suggested assignment: Semantics.Strings/Validation/Attributes/Format/ (HasMinimumLines, HasMaximumLines, HasExactLines)
    • Duplicates: none found among open issues in the org
    • In progress: no matching open PR

    Notes: A shared CountLines helper, matching the break definition IsMultiLine/IsSingleLine already use, fixes all three attributes at once. Deleting the no-op CRLF block is a free cleanup.


    Generated by Claude Code

  2. added a commit that references this issue on Oct 14, 2026
    92737ca
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingreadyFully specified; implement as written

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions