Skip to content

Fix three bugs in php-complete function name completion - #825

Merged
zonuexe merged 4 commits into
masterfrom
fix/php-complete-bugs
Aug 4, 2026
Merged

Fix three bugs in php-complete function name completion#825
zonuexe merged 4 commits into
masterfrom
fix/php-complete-bugs

Conversation

@zonuexe

@zonuexe zonuexe commented Aug 4, 2026

Copy link
Copy Markdown
Member

Three independent bugs in php-complete.el, each with a regression test.

The :safe predicate of php-complete-function-modules accepted anything, and never ran

The predicate looped over values, a standard Emacs variable that is bound to nil, instead of over its own argument value. cl-loop for v in nil always ... is true for an empty list, so any list passed as safe, including '(bogus-module) and '("anything" 42).

It also failed before it could reject anything. Emacs checks .dir-locals.el values before loading php-complete.el, so the predicate runs as copied into php-mode-autoloads.el, where neither cl-loop nor php-defs-functions-alist is available:

php-complete-function-modules -> (void-function cl-loop)

safe-local-variable-p wraps predicates in with-demoted-errors, which turns that error into nil, so Emacs asked for confirmation in every project that set the variable.

This branch adds an autoloaded php-defs-function-module-names for the predicate to consult, and rewrites the predicate without cl-lib. Autoloading php-defs-functions-alist itself would copy 3800 lines into the autoloads file, so the new variable holds the module symbols alone. It also feeds the defcustom :type, and a test keeps it in sync with the alist.

php-complete--functions sorted a user option in place

sort is destructive, so building the cache key reordered the value the user had set. A configured (pcntl bcmath core) came back as (bcmath core pcntl) after one completion. It now sorts a copy.

Module names leaked into the completion candidates

Entries of php-defs-functions-alist are (MODULE . FUNCTION-NAMES), and the code appended the whole entry, splicing MODULE in among the function names. With php-complete-function-modules set to (bcmath), the symbol bcmath showed up next to "bcadd". Taking the cdr fixes it. php-defs-functions-alist also gets the docstring it should have had as a public variable, spelling out that shape.

Verification

The 5 new ERT tests pass on this branch and fail on master, including a subprocess emacs -Q --batch --load ...autoloads check that covers the cold path of the :safe predicate. eask test ert ./tests/php-mode-test.el reports 71 tests, 0 unexpected, 2 skipped. Byte compilation is clean, and checkdoc reports no new warnings.

zonuexe added 4 commits August 4, 2026 22:24
The predicate had two independent defects.

It looped over `values' -- a standard (Emacs 29+ obsolete) Emacs variable
that is nonetheless bound, normally to nil -- instead of over its own
argument `value'.  `cl-loop for v in nil always ...' is vacuously true,
so the predicate returned t for every list, and a .dir-locals.el could
put arbitrary unvetted module names into the variable:

    pred '(core)          -> t
    pred '(bogus-module)  -> t   ; should be nil
    pred '("anything" 42) -> t   ; should be nil

It also never ran successfully in the first place.  Emacs decides whether
a .dir-locals.el value is safe while hacking local variables, which
happens before php-complete.el is loaded, so the predicate executes as
copied into php-mode-autoloads.el -- where neither cl-lib nor
php-defs-functions-alist exists yet:

    php-complete-function-modules -> (void-function cl-loop)

safe-local-variable-p demotes that error and returns nil, so the variable
was treated as unsafe and prompted for confirmation anyway.

Add an autoloaded `php-defs-function-module-names' for the predicate to
consult -- a plain list of module symbols, rather than autoloading the
3800-line `php-defs-functions-alist' itself -- and rewrite the predicate
without cl-lib.  `php-defs-function-module-names' also feeds the
defcustom `:type', and a test keeps it in sync with the alist.
`sort' is destructive, so sorting `php-complete-function-modules' to
build the cache key reordered the value the user had set: after a single
completion, a configured (pcntl bcmath core) came back as
(bcmath core pcntl).  Sort a copy instead.
An entry of `php-defs-functions-alist' is (MODULE . FUNCTION-NAMES), so
appending the `assq' result spliced MODULE into the candidate list: with
`php-complete-function-modules' set to (bcmath), the symbol `bcmath'
showed up among the function name strings.  Take the `cdr'.

Also give `php-defs-functions-alist' the docstring it should have had as
a public variable, spelling out that shape.
Emacs 28 was the first to copy a defcustom's `:safe' predicate into the
generated autoloads file (autoload.el, "Propagate the :safe property to
the loaddefs file").  Emacs 27's generator converts the defcustom into a
plain defvar and discards `:safe' entirely, so the autoloads file has no
`safe-local-variable' property to call and the subprocess died with
(void-function nil) -- the CI failure on the 27.2 jobs.

Skip the test when the generated file carries no such property, and check
the subprocess output before its exit status so that a real failure
reports what the subprocess complained about.

Note in the CHANGELOG that this half of the fix needs Emacs 28 or later.
@zonuexe
zonuexe merged commit 4bc3f18 into master Aug 4, 2026
14 checks passed
@zonuexe
zonuexe deleted the fix/php-complete-bugs branch August 4, 2026 15:58
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