ek9_push_down
Removes the named methods/operators from a superclass and copies them into each named direct subclass; answers status, edits[] (one DELETE + one INSERT per subclass) and conflicts[] (subclass-not-direct, target-is-only-source-class).
When to use it
'Move fly() from Shape down into Eagle and Sparrow' is one call with symbol:'Shape', members:['fly'], subclasses:['Eagle','Sparrow'] - no file or line needed. Use it when a superclass member is only needed by some direct subclasses (the abstraction was over-promoted).
When not to use it
- Promoting a member UP from subclasses to the superclass (the opposite) - use
ek9_pull_up. - Removing the member from the hierarchy entirely, with no subclass needing it - use
ek9_safe_delete(also the fix when the source class has no subclasses). - The fix is access-level, not relocation (e.g. make it private/internal) - use
ek9_change_visibility. - Traits, not classes - traits are behaviour contracts with implementors, not subclasses; edit the trait directly and use
ek9_query_referencesto find consumers.
Details
Class hierarchies ONLY, methods/operators ONLY (fields not yet supported); each named DIRECT subclass gets an identical copy of every member (no per-subclass body customisation). 'override' is DROPPED from copies by default (no parent left to override, else E05100); set keepAsOverride:true ONLY for multi-level hierarchies where an ancestor above the source still declares the member. status:conflicts kinds: subclass-not-direct, target-is-only-source-class; external callers reaching the member via the superclass type surface as status:would-fail from the scratch compile.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
file | string | The EK9 source file - OPTIONAL when symbol is a construct name; required to anchor on a member or a use site. | |
keepAsOverride | boolean | Keep the 'override' modifier on the subclass copies (only for multi-level hierarchies where an ancestor above the superclass still declares the member). | |
line | integer | 1-based line of the declaration or a reference - OMIT it and pass symbol:'<name>' to let the tool find the line (a model counting lines in a file miscounts); if you do count, use ek9_read_file(lineNumbers:true). | |
members | array of string | yes | Names of methods/operators on the superclass to push down. Operators use their symbol form (e.g. '==', '<=>'). |
subclasses | array of string | yes | Names of DIRECT subclasses to receive the pushed-down members. Empty errors. |
symbol | string | A construct NAME alone ('Shape', 'sample.deps.core::Validator') - no file needed, the tool finds it workspace-wide; or, with file (+ line), the identifier you mean there. |
It also takes the arguments every tool takes.
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it.
Subclass not direct conflict
Request
{
"file" : "shapes.ek9",
"line" : 6,
"members" : [ "area" ],
"subclasses" : [ "Square", "NotASubclass" ]
}
Response
{
"conflicts" : [ {
"kind" : "subclass-not-direct",
"message" : "Not direct subclasses of 'Shape': NotASubclass (direct subclasses: Circle, Square)"
} ],
"edits" : [ ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 0,
"lineCount" : 0,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "conflicts",
"summary" : "Not all subclasses are direct subclasses of Shape",
"workspaceHash" : "<workspace-hash>",
"wouldBeDiagnostics" : [ ],
"written" : false
}
Apply to subclass
Request
{
"file" : "shapes.ek9",
"line" : 6,
"members" : [ "area" ],
"subclasses" : [ "Circle" ]
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"before" : " area()\n <- rtn as Float: 0.0",
"endLine" : 9,
"file" : "shapes.ek9",
"startLine" : 8,
"type" : "delete"
}, {
"after" : "\n area()\n <- rtn as Float: 0.0",
"endLine" : 14,
"file" : "shapes.ek9",
"startLine" : 14,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "high",
"fileCount" : 1,
"lineCount" : 3,
"touchesPublicApi" : true,
...
Cut short here: 8 more lines. The whole recorded call.
Dry run ready
Request
{
"dryRun" : true,
"file" : "shapes.ek9",
"line" : 6,
"members" : [ "area" ],
"subclasses" : [ "Circle" ]
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"before" : " area()\n <- rtn as Float: 0.0",
"endLine" : 9,
"file" : "shapes.ek9",
"startLine" : 8,
"type" : "delete"
}, {
"after" : "\n area()\n <- rtn as Float: 0.0",
"endLine" : 14,
"file" : "shapes.ek9",
"startLine" : 14,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "high",
"fileCount" : 1,
"lineCount" : 3,
"touchesPublicApi" : true,
...
Cut short here: 8 more lines. The whole recorded call.
See also
- Show me in the IDE: Push Down