You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
False positive: java/field-masks-super-field triggered on Kotlin sealed class with open val constructor parameters #22361
The rule java/field-masks-super-field is producing a false positive on a Kotlin sealed class that uses open val constructor parameters. No actual field shadowing exists in the source code.
CodeQL Version
GitHub Advanced Security (cloud) - latest on github.dev
"This field shadows another field called width/height in a superclass."
Rule ID: java/field-masks-super-field
Why This Is a False Positive
No subclass redeclares width or height in its body
Every object subclass simply passes values via the constructor to the parent
There is no Java-style field shadowing at the source level
The alert appears to be triggered by synthetic bridge method scaffolding
that Kotlin generates for open val properties, which the Java extractor
misidentifies as a field declaration in the subclass
Related
This appears to be in the same category as PR #10859 which excluded Kotlin Live Literals from this same rule: #10859
That PR acknowledged that Kotlin-generated bytecode patterns can trigger false positives in java/field-masks-super-field. The sealed class + object + open val pattern appears to be another such case.
Workaround
Removing open from the constructor parameters eliminates the alert and is safe when no subclass actually overrides the properties. However
this forces unnecessary code changes to work around a false positive.
Expected Behaviour
The rule should not fire when no subclass explicitly redeclares the field in its body - consistent with how PR #10859 handled Live Literals.
I'm not able to reproduce the problem based on the minimal reproducer you provided. When I create a test with that code, and run java/field-masks-super-field there are no results.
Thank you for checking! That actually makes sense - I've done further investigation since opening this issue
and I believe the root cause is different from what I
described in the original report.
After opening the actual alerts in the GitHub UI browser, the real shadowed field name shown is $stable - not width or height. Every single one of our 1118 alerts shows $stable as the shadowed field.
$stable is a synthetic field injected by the Jetpack Compose compiler plugin into every class it processes
for stability inference. Because Compose adds it to both parent and child classes with the same name, CodeQL fires
the rule - even though no developer wrote either field.
So the actual reproducer is:
Any sealed class or class hierarchy where the Compose compiler has processed the classes
Both parent and subclass get $stable injected
CodeQL sees same field name in both → fires the rule
This is the same category as the LiveLiteral exclusion in PR #10859 — Compose-generated synthetic fields — but $stable was not covered by that fix.
Could the fix be extended to also exclude $stable fields, similar to how LiveLiteral is already excluded?
Checking every field against the query's own filter logic (not this.isPrivate() and not this.isStatic()):
Field
In parent
In subclass
Static
Private
Can rule fire
$stable
yes
yes
yes, public static final
no
no — excluded by not this.isStatic()
fnfWidgetInsertPos
no
yes
no
yes, private final
no — excluded by not this.isPrivate()
all other fields
no
yes
no
yes, private final
no — excluded by not this.isPrivate()
There's no field in this hierarchy that's both non-static and non-private. Under the query's own logic, the rule shouldn't be able to fire here at all.
Is it possible CodeQL's Kotlin extractor represents $stable as a non-static field internally, even though the compiled bytecode declares it as public static final int $stable? That would explain the mismatch between what javap shows and what the alert reports.
We have 1118 open alerts, all showing $stable as the shadowed field. Happy to provide any more detail or run further checks on our end if that helps.
Note that I have no way to inspect the issues you're linking to, as they are internal to you organization.
There are two paths forward here:
either you provide all the necessary steps here to reproduce the problem, or
you open a support request with GitHub support and share a database with them in private; it seems you're a paying customer and hence should be able to do so.
Activity
jketema commentedon Aug 17, 2026
Hi @shreyasingh824,
I'm not able to reproduce the problem based on the minimal reproducer you provided. When I create a test with that code, and run
java/field-masks-super-fieldthere are no results.shreyasingh824 commentedon Aug 18, 2026
Hi @jketema,
Thank you for checking! That actually makes sense - I've done further investigation since opening this issue
and I believe the root cause is different from what I
described in the original report.
After opening the actual alerts in the GitHub UI browser, the real shadowed field name shown is
$stable- notwidthorheight. Every single one of our 1118 alerts shows$stableas the shadowed field.$stableis a synthetic field injected by the Jetpack Compose compiler plugin into every class it processesfor stability inference. Because Compose adds it to both parent and child classes with the same name, CodeQL fires
the rule - even though no developer wrote either field.
So the actual reproducer is:
$stableinjectedThis is the same category as the
LiveLiteralexclusion in PR #10859 — Compose-generated synthetic fields — but$stablewas not covered by that fix.Could the fix be extended to also exclude
$stablefields, similar to howLiveLiteralis already excluded?Thanks again for looking into this.
jketema commentedon Aug 18, 2026
Again. I cannot reproduce this. You need to provide a complete example with reproducing steps.
shreyasingh824 commentedon Aug 18, 2026
Hi @jketema,
I ran
javapon the actual compiled classes from the flagged file to get the ground truth on the bytecode.Alert #9135 —
FnFInterruptState.ktAlert #9136 —
ExperimentationCoreApplicationManager.ktBoth alerts show:
Here's the actual
javapoutput for the class hierarchy:FnFInterruptState(parent class):FnFInterruptState$Success(subclass):FnFInterruptState$Loading(subclass):Checking every field against the query's own filter logic (
not this.isPrivate()andnot this.isStatic()):$stablepublic static finalnot this.isStatic()fnfWidgetInsertPosprivate finalnot this.isPrivate()private finalnot this.isPrivate()There's no field in this hierarchy that's both non-static and non-private. Under the query's own logic, the rule shouldn't be able to fire here at all.
Is it possible CodeQL's Kotlin extractor represents
$stableas a non-static field internally, even though the compiled bytecode declares it aspublic static final int $stable? That would explain the mismatch between whatjavapshows and what the alert reports.We have 1118 open alerts, all showing
$stableas the shadowed field. Happy to provide any more detail or run further checks on our end if that helps.jketema commentedon Aug 18, 2026
Note that I have no way to inspect the issues you're linking to, as they are internal to you organization.
There are two paths forward here: