@@ -162,3 +162,42 @@ in `parse_inflight` and nothing in the log but one panic line.
1621622 . Wrap the fast path of ` with_parsed_program ` in ` catch_unwind `
163163 like the slow path (evicting the poisoned parse-cache entry on
164164 panic so the next call re-parses).
165+
166+
167+ ## B27. String literal type comparison is quote-style sensitive
168+
169+ ** Severity: Low (false-positive argument diagnostic) · Confirmed**
170+
171+ ` literal_is_subtype_of ` (` src/php_type.rs:3297-3369 ` ) compares two
172+ ` PhpType::Literal ` string values with plain ` lit == other_lit ` . Both
173+ the argument-narrowing path
174+ (` src/diagnostics/type_errors.rs ` , argument literal narrowing) and
175+ the docblock literal-type parser
176+ (` src/php_type.rs:4095 ` , ` ast::Type::LiteralString ` ) build the
177+ ` Literal ` payload from the ** raw source text including quote
178+ characters** , so a double-quoted argument literal never equals a
179+ single-quoted docblock literal even when their unquoted contents are
180+ identical.
181+
182+ ** Trigger:**
183+
184+ ``` php
185+ /** @param 'asc'|'desc' $direction */
186+ function orderBy(string $column, string $direction): void {}
187+
188+ function test(): void {
189+ orderBy('id', "desc"); // flagged: "desc" (double-quoted) != 'desc' (single-quoted)
190+ }
191+ ```
192+
193+ Single-quoted usage (` orderBy('id', 'desc') ` ) is unaffected, since
194+ both sides happen to agree on quote style in that case.
195+
196+ ** Fix:** compare the two literals by their unquoted content instead
197+ of raw source text. Both ` LiteralString ` (source) and
198+ ` LiteralStringType ` (docblock) already carry a parsed, unquoted
199+ ` value ` field alongside ` raw ` — normalise through that (or strip
200+ quotes consistently) before the equality check in
201+ ` literal_is_subtype_of ` , and make sure the ` PhpType::Literal `
202+ constructors that currently pass through ` raw ` for strings do the
203+ same.
0 commit comments