Hi.
I've had an issue with phpactor (installed in Zed via the php extension) and found a workaround (with claude's help 😉) , but this looks like a potential issue:
Summary
language_server.diagnostic_ignore_codes does not filter diagnostics produced by outsourced providers. Since language_server.diagnostic_outsource defaults to true, this means the ignore list silently fails for worse.* (worse_reflection) diagnostics — the most common ones users want to suppress.
Version
Phpactor 2025.12.21.1 (also reproducible against current master at time of writing).
Reproduction
-
In any PHP file, define a method without a @param docblock so worse.docblock_missing_param triggers:
<?php
class Foo {
public function bar(array $arr): bool { return true; }
}
-
Add to .phpactor.json:
{
"language_server.diagnostic_ignore_codes": [
"worse.docblock_missing_param"
]
}
-
Restart the language server and open the file.
Expected: the worse.docblock_missing_param diagnostic is suppressed.
Actual: the diagnostic still appears in the editor.
Workaround
Disable outsourcing:
{
"language_server.diagnostic_outsource": false,
"language_server.diagnostic_ignore_codes": ["worse.docblock_missing_param"]
}
With outsourcing off, the filter works as documented.
Root cause
In lib/Extension/LanguageServer/LanguageServerExtension.php, the CodeFilteringDiagnosticProvider wrapper is only applied to providers collected for the in-process DiagnosticsEngine:
// LanguageServerExtension.php (~L550-582)
$container->register(DiagnosticsEngine::class, function (Container $container) {
$providers = $this->collectDiagnosticProviders(
$container,
outsourced: $container->parameter(self::PARAM_DIAGNOSTIC_OUTSOURCE)->bool() ? false : null,
);
// ...
if (count($ignoreCodes)) {
$providers = array_map(function (DiagnosticsProvider $provider) use ($ignoreCodes) {
return new CodeFilteringDiagnosticProvider($provider, $ignoreCodes);
}, $providers);
}
return new DiagnosticsEngine(/* wrapped providers */);
});
The separately-registered AggregateDiagnosticsProvider (used by the outsourced subprocess, ~L592+) is not wrapped with CodeFilteringDiagnosticProvider, so diagnostics produced there bypass the filter entirely.
Suggested fix
Apply the CodeFilteringDiagnosticProvider (and PathExcludingDiagnosticsProvider) wrappers in both registration paths — or apply them at a layer common to both, e.g. inside the outsourced subprocess's provider collection.
Notes
- The filter logic itself works correctly when reached — confirmed by setting
diagnostic_outsource: false.
- No log/warning is emitted to indicate the filter was skipped, making this hard to diagnose. A debug log when ignore codes are configured but a provider is outsourced would help.
- Documentation for
diagnostic_ignore_codes ("Ignore diagnostics that have the codes listed here...") doesn't mention this limitation.
`
Hi.
I've had an issue with phpactor (installed in Zed via the php extension) and found a workaround (with claude's help 😉) , but this looks like a potential issue:
Summary
language_server.diagnostic_ignore_codesdoes not filter diagnostics produced by outsourced providers. Sincelanguage_server.diagnostic_outsourcedefaults totrue, this means the ignore list silently fails forworse.*(worse_reflection) diagnostics — the most common ones users want to suppress.Version
Phpactor
2025.12.21.1(also reproducible against current master at time of writing).Reproduction
In any PHP file, define a method without a
@paramdocblock soworse.docblock_missing_paramtriggers:Add to
.phpactor.json:{ "language_server.diagnostic_ignore_codes": [ "worse.docblock_missing_param" ] }Restart the language server and open the file.
Expected: the
worse.docblock_missing_paramdiagnostic is suppressed.Actual: the diagnostic still appears in the editor.
Workaround
Disable outsourcing:
{ "language_server.diagnostic_outsource": false, "language_server.diagnostic_ignore_codes": ["worse.docblock_missing_param"] }With outsourcing off, the filter works as documented.
Root cause
In
lib/Extension/LanguageServer/LanguageServerExtension.php, theCodeFilteringDiagnosticProviderwrapper is only applied to providers collected for the in-processDiagnosticsEngine:The separately-registered
AggregateDiagnosticsProvider(used by the outsourced subprocess, ~L592+) is not wrapped withCodeFilteringDiagnosticProvider, so diagnostics produced there bypass the filter entirely.Suggested fix
Apply the
CodeFilteringDiagnosticProvider(andPathExcludingDiagnosticsProvider) wrappers in both registration paths — or apply them at a layer common to both, e.g. inside the outsourced subprocess's provider collection.Notes
diagnostic_outsource: false.diagnostic_ignore_codes("Ignore diagnostics that have the codes listed here...") doesn't mention this limitation.`