Detect the _deprecated_file() call anywhere in a file's uses - #281
Detect the _deprecated_file() call anywhere in a file's uses#281sirreal wants to merge 2 commits into
Conversation
sirreal
left a comment
There was a problem hiding this comment.
Note
This is an agentic review, generated by Claude Code at the repository owner's request.
Ready to land.
Verified
-
CI green on 7.4 and 8.4; mergeable, clean, based on
master. -
Both bugs are real.
masterreads$file['uses']['functions'][0]unconditionally insideisset( $file['uses']['functions'] )— an empty-but-set uses list is an undefined offset (notice on 7.4, warning on 8.x), and it then reads['deprecation_version']without anissetguard. The index-0-only check means any call preceding_deprecated_file()drops the flag. -
The non-regression claim checks out. I walked the four cases against the pre-#277 exporter, which writes
deprecation_versionontouses.functions[0]regardless of which record triggered it:_deprecated_fileat index 0 → old code and new code read the same record, same version. Identical._deprecated_filelater → old code never looks past index 0 and does not flag; new code finds the record but it carries nodeprecation_version(the version sits on record 0), so$deprecated_filestaysfalseand it still does not flag. Identical.- Two deprecating calls → the loop breaks on the first
_deprecated_file, which is also where pre-#277 put the version. Identical.
So the change is a strict superset: it never flags less than today, and it starts flagging the missed cases once #277 places the version on the right record. The claim in the description is accurate.
-
The falsy path is unchanged downstream.
import_file()setsfile_meta['deprecated']and the consumer atclass-importer.php:742isif ( ! empty( $this->file_meta['deprecated'] ) ).masterproducednull(plus a notice) for a version-less_deprecated_file; this producesfalse. Both falsy, no behavior change — and the notice is gone. -
isset( $function_use['name'] )plus===also makes this safe against the non-stringnamevalues that dynamic calls can produce (the importer already special-cases those elsewhere). -
Tests pin the contract, and two are RED on master (missed flag, undefined offset), as claimed. Feeding hand-built uses arrays rather than parsing a fixture is the right call here: it tests the importer's contract independently of where the exporter happens to put the version, which is precisely the seam #277 moves.
Worth noting
- Nothing blocking. One observation for the record: this is importer-side code, so the corpus-diff check in #284 will never cover it — the corpus diff compares exporter output only. Unit tests are the whole safety net for this file, which makes the five added here worth having.
- The
deprecatedentry is stuffed into$data['doc']['tags']as a string-keyed member of what is otherwise a numeric list of tag records. Pre-existing, out of scope, but it is why the test reads$tags['deprecated']rather than searching the list — worth cleaning up someday.
The importer flags a file as deprecated only when
uses.functions[0]— the first recorded call in the file — is_deprecated_file(). Any call preceding it silently loses the deprecation flag, and an empty uses list raises an undefined-offset notice. The importer now searches the uses list for the_deprecated_filerecord and readsdeprecation_versionfrom that record.Complements #277, which makes the exporter attach
deprecation_versionto the deprecating call's own record instead of index 0. With pre-#277 export data where the deprecating call isn't first, the version sits on the wrong record and the file stays unflagged — the same outcome as today, so this change is non-regressing under either exporter and becomes fully correct once #277 lands. The tests feed hand-built uses data to the importer, so they exercise the importer's contract without depending on the exporter's placement.The after-other-calls test and the empty-uses test fail on master (one missed flag, one undefined-offset error); all pass with the fix, as does the full suite.
Found during the review of #262; extracted as a standalone change.
🤖 Generated with Claude Code