Skip to content

fix(db): bind catch(Exception) to a class that exists, and drop dead SetupHandler - #3233

Merged
rubenvdlinde merged 2 commits into
developmentfrom
fix/dead-setuphandler-and-unbindable-catch
Aug 31, 2026
Merged

fix(db): bind catch(Exception) to a class that exists, and drop dead SetupHandler#3233
rubenvdlinde merged 2 commits into
developmentfrom
fix/dead-setuphandler-and-unbindable-catch

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Two entries the Psalm baseline was carrying as accepted noise. Neither is noise. 187 -> 184.

A catch block that could never catch

lib/Db/Register.php caught Exception with no import, inside namespace OCA\OpenRegister\Db. PHP resolves an unqualified catch type against the current namespace, so it was bound to OCA\OpenRegister\Db\Exception — which does not exist — and the block was unreachable.

Verified rather than reasoned about:

namespace Demo\Sub;
try { throw new \RuntimeException("boom"); }
catch (Exception $e) { return "CAUGHT"; }   // no import
// -> NOT CAUGHT -> RuntimeException: boom

use Exception; binds it to the global class the code plainly meant.

I checked the rest of lib/ for the same shape. ObjectsController (6 sites) and Repair/RenameDutchColumns (2 sites) also catch a bare Exception, but both import OCP\DB\Exception, so those bind to a real class and are deliberate. Psalm had flagged exactly the one file that was broken — my grep was the crude instrument, not Psalm.

Dead API surface

SettingsService declared a property, a constructor parameter and two docblocks for SetupHandler, a class that exists nowhere in the tree. It was assigned and never read: dead surface that would fatal the moment anything tried to supply it. Removed, with the setupHandler: null argument in Application.php and the matching positional null in the two tests that construct the service positionally.

Verification

vendor/bin/psalm 5.26.1 on PHP 8.3: with the baseline emptied, errors went 187 -> 184; the regenerated baseline is green.

PHPUnit was not run locally — it needs the Nextcloud server bootstrap, which only exists inside a server checkout. CI covers it.

Conduction Release Bot added 2 commits August 31, 2026 18:21
…SetupHandler

Two findings that psalm-baseline.xml had been carrying as accepted noise.
Neither is noise. 187 -> 184.

lib/Db/Register.php caught `Exception` with no import, inside namespace
OCA\OpenRegister\Db. PHP resolves an unqualified catch type against the
CURRENT namespace, so that block was bound to OCA\OpenRegister\Db\Exception,
which does not exist, and it could never catch anything. Verified rather
than reasoned about:

    namespace Demo\Sub;
    try { throw new \RuntimeException("boom"); }
    catch (Exception $e) { ... }   // no import
    -> NOT CAUGHT -> RuntimeException: boom

Adding `use Exception;` binds it to the global class the code plainly meant.
Checked the rest of lib/: ObjectsController and Repair/RenameDutchColumns
also catch a bare `Exception`, but both import OCP\DB\Exception, so their
catches bind to a real class and are deliberate. Psalm flagged exactly the
one file that was broken.

lib/Service/SettingsService.php declared a property, a constructor
parameter and two docblocks for SetupHandler, a class that exists nowhere
in the tree. It was written and never read, so it was dead API surface
that would fatal the moment anything tried to supply it. Removed, along
with the `setupHandler: null` argument in Application.php and the matching
positional null in the two tests that build the service positionally.

Verified with the repo own vendor/bin/psalm 5.26.1 on PHP 8.3: errors with
the baseline emptied went 187 -> 184, and the regenerated baseline is green.
PHPUnit was NOT run here: it needs the Nextcloud server bootstrap, which
only exists inside a server checkout.
Removing the SetupHandler parameter shifted every later POSITIONAL
argument, and I checked for that by reading the first line after each
`new SettingsService(` and concluding the file used named arguments. It
uses both. Five calls further down SettingsServiceTest.php are positional,
and CI caught it as 13 TypeErrors, all the same one:

  Argument #14 ($appName) must be of type string, MockObject_IAppContainer
  given

which is the shift saying exactly what it was.

Verified by position this time rather than by sampling: with the parameter
gone, appName is slot 14, and all seven positional call sites across the
three test files now carry a string literal there.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ b27d231

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-specs
test-l10n
test-l10n-parity
format
check-schema-l10n
check-l10n-js
composer ✅ 174/174
npm ✅ 543/543
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-08-31 16:39 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 8925ea4

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-specs
test-l10n
test-l10n-parity
format
check-schema-l10n
check-l10n-js
composer ✅ 174/174
npm ✅ 543/543
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-08-31 16:48 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Merging with the coverage ratchet red, deliberately, and with the reason stated rather than waved through.

The PHPUnit suite itself passes: 17850 tests, 40089 assertions, 0 failures, 0 errors. The job fails only on the ratchet:

FAIL: coverage of the code this change KEEPS or ADDS dropped by 0.02%.

That is arithmetic, not a regression. This PR deletes 13 lines, and those lines were executed by the existing tests: the $this->setupHandler = $setupHandler; assignment ran on every construction, and the property and parameter were reached. Removing covered lines lowers the covered numerator while the uncovered denominator is unchanged, so the ratio dips. Nothing became less tested; there is simply less code.

I could have moved the number by adding a test, but a test written to offset a metric rather than to check a behaviour is worse than the 0.02%.

The two real defects this PR fixes are both in the earlier description: a catch bound to a class that does not exist, proven unreachable with a standalone PHP repro, and a constructor parameter typed to a class that exists nowhere in the tree.

@rubenvdlinde
rubenvdlinde merged commit b957cae into development Aug 31, 2026
45 of 47 checks passed
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