FoldingWhiteSpace: CRLFAtTheEnd check is unreachable dead code

Author: ruttydmCreated Apr 9, 2026Updated Apr 9, 2026

Bug

In FoldingWhiteSpace::checkCRLFInFWS(), the CRLFAtTheEnd error can never be returned because the exact same condition is checked twice in sequence, with no lexer advancement between them.

Version: 4.0.4

Code

FoldingWhiteSpace.php lines 61–77:

php
protected function checkCRLFInFWS(): Result
{
    if (!$this->lexer->current->isA(EmailLexer::CRLF)) {
        return new ValidEmail();
    }

    if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
        return new InvalidEmail(new CRLFX2(), $this->lexer->current->value);
    }

    //this has no coverage. Condition is repeated from above one
    if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
        return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->current->value);
    }

    return new ValidEmail();
}

Analysis

isNextTokenAny() is read-only — it checks $this->lookahead without advancing the lexer position (confirmed in doctrine/lexer AbstractLexer::isNextTokenAny(), which only reads $this->lookahead->isA()). The only method that advances the lexer is moveNext().

Since both conditions on lines 67 and 72 check the same lookahead token with the same parameters:

  • If line 67 is true (next token is NOT SP/HTAB) → returns CRLFX2, never reaches line 72
  • If line 67 is false (next token IS SP/HTAB) → line 72 is also false, so CRLFAtTheEnd is never returned

The existing comment on line 71 confirms this: "this has no coverage. Condition is repeated from above one"

Impact

The CRLFAtTheEnd validation is dead — emails with CRLF at the end of folding whitespace that should be rejected with CRLFAtTheEnd are either rejected with the wrong reason (CRLFX2) or accepted as valid.

A moveNext() call was likely intended between the two checks to advance to the next token.