Skip to content

Map JSON false and null to nil in phpstan--parse-json - #70

Merged
zonuexe merged 1 commit into
masterfrom
fix/json-false-object
Jul 17, 2026
Merged

Map JSON false and null to nil in phpstan--parse-json#70
zonuexe merged 1 commit into
masterfrom
fix/json-false-object

Conversation

@zonuexe

@zonuexe zonuexe commented Jul 17, 2026

Copy link
Copy Markdown
Member

Fixes a shared JSON-parsing bug that made non-ignorable PHPStan errors look ignorable — found while adding the flymake tests in #69.

The bug

phpstan--parse-json returned a truthy symbol for JSON false: json-parse-buffer gives :false, the json-read-object fallback gives :json-false. PHPStan's ignorable flag is read in a truthy context, so a non-ignorable message ("ignorable":false, e.g. a parse error) was treated as ignorable by every consumer:

  • flycheck-phpstan and flymake-phpstan appended the identifier with the 🪪 prefix, which tells the user "you can @phpstan-ignore this";
  • phpstan-update-ignorebale-errors-from-json-buffer collected it, so phpstan-insert-ignore offered to ignore an error PHPStan says cannot be ignored.

json-parse-buffer also returns a truthy :null for JSON null, while the fallback returns nil — so the two paths disagreed, and a :null reaching concat (an absent tip, say) would error on a modern Emacs.

;; message with "ignorable":false
(plist-get msg :ignorable)   ; => :false  (truthy!)  — now => nil

The fix

Pass :false-object nil :null-object nil to json-parse-buffer, and bind json-false/json-null to nil for the json-read-object fallback. Both paths now map false and null to nil.

I checked every consumer first: nothing uses plist-member, so no code distinguishes false from an absent key; the other JSON reads are numbers or strings guarded with numberp (in phpstan-hover), so nil is safe throughout. This also makes the two parser paths agree on null.

Result

  • flycheck-phpstan: a non-ignorable parse error no longer shows 🪪; an ignorable error still does.
  • flymake-phpstan: same.
  • phpstan-insert-ignore no longer offers non-ignorable identifiers.

Tests

Four regression tests, each verified to fail when the mapping is reverted:

  • phpstan-test-parse-json-false-is-nilfalse/null read as nil, true as t.
  • phpstan-test-update-ignorable-skips-non-ignorablephpstan--ignorable-errors excludes the non-ignorable message.
  • flycheck-phpstan-test-identifier-only-when-ignorable and flymake-phpstan-test-parse-skips-non-ignorable — the identifier appears only for the ignorable one.

25 tests total pass on system Emacs and Emacs 27.2; compile is warning-free on both.

PHPStan's `ignorable' flag is read in a truthy context, but both JSON
parsers returned a truthy symbol for `false': `json-parse-buffer' gives
`:false' and the `json-read-object' fallback gives `:json-false'.  So a
non-ignorable message (`"ignorable":false') was treated as ignorable by
`flycheck-phpstan', `flymake-phpstan', and
`phpstan-update-ignorebale-errors-from-json-buffer': its identifier was
shown with the 🪪 prefix and `phpstan-insert-ignore' offered it, even
though PHPStan says it cannot be ignored.

`json-parse-buffer' also returns a truthy `:null' for JSON null, while the
fallback returns nil, so the two paths disagreed -- and a `:null' reaching
`concat' (say an absent tip) would error on a modern Emacs.

Pass `:false-object nil :null-object nil' to `json-parse-buffer', and bind
`json-false'/`json-null' to nil for the fallback, so both map `false' and
`null' to nil.  No consumer distinguishes `false' from an absent key (none
use `plist-member'), and the remaining reads are numbers or strings
guarded with `numberp', so nil is safe throughout.

Tests cover the parse result, the ignorable-errors side effect, and both
backends' identifier handling; each fails when the mapping is reverted.
@zonuexe
zonuexe merged commit 47e2604 into master Jul 17, 2026
9 checks passed
@zonuexe
zonuexe deleted the fix/json-false-object branch July 17, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant