Skip to content

Commit

Permalink
Merge pull request #1631 from DanielXMoore/readonly-at
Browse files Browse the repository at this point in the history
Fix mixing access modifiers (e.g. `readonly`) with `@` arguments in constructors
  • Loading branch information
edemaine authored Dec 3, 2024
2 parents 9b5b016 + 84dcc4e commit abbf5dc
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 14 deletions.
2 changes: 2 additions & 0 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,7 @@ NWBindingIdentifier
type: "AtBinding",
children: [ref],
ref,
names: [],
}
# Likewise for private identifiers
Hash AtIdentifierRef:ref ->
Expand All @@ -2009,6 +2010,7 @@ NWBindingIdentifier
type: "AtBinding",
children: [ref],
ref,
names: [],
}
Identifier:id
# NOTE: Support for return := 1 and let return: number
Expand Down
16 changes: 6 additions & 10 deletions source/parser/binding.civet
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {
ArrayBindingPattern
ASTNode
ASTNodeObject
AtBinding
BindingPattern
BindingRestElement
Children
Expand Down Expand Up @@ -113,30 +112,27 @@ function gatherBindingCode(statements: ASTNode, opts?: { injectParamProps?: bool

function insertRestSplices(s, p: unknown[], thisAssignments: ThisAssignments): void
gatherRecursiveAll(s, (n) => n.blockPrefix or (opts?.injectParamProps and n.accessModifier) or n.type is "AtBinding")
.forEach((n) => {
.forEach (n) =>
// Insert `this` assignments
if n.type is "AtBinding"
{ ref } := n as! AtBinding
{ id } := ref
thisAssignments.push([`this.${id} = `, ref])
return

if (opts?.injectParamProps and n.type is "Parameter" and n.accessModifier)
n.names.forEach((id) => {
thisAssignments.push({
type: "AssignmentExpression",
children: [`this.${id} = `, id],
if opts?.injectParamProps and n.type is "Parameter" and n.accessModifier
for each id of n.names
thisAssignments.push
type: "AssignmentExpression"
children: [`this.${id} = `, id]
js: true
})
})
return

{ blockPrefix } := n
p.push(blockPrefix)

// Search for any further nested splices, and at bindings
insertRestSplices(blockPrefix, p, thisAssignments)
})

insertRestSplices(statements, splices, thisAssignments)

Expand Down
11 changes: 8 additions & 3 deletions source/parser/function.civet
Original file line number Diff line number Diff line change
Expand Up @@ -843,21 +843,26 @@ function processParams(f): void
index-- while classExpressions[index-1]?[1] is like {type: "MethodDefinition", name: "constructor"}
fStatement := classExpressions[index]
for each parameter of gatherRecursive parameters, .type is "Parameter"
continue unless parameter.typeSuffix
{accessModifier} := parameter
continue unless accessModifier or parameter.typeSuffix
for each binding of gatherRecursive parameter, .type is "AtBinding"
// TODO: Handle typeSuffix of entire complex parameter pattern
// (Currently just handle `@prop ::` individual typing,
// where parent is AtBindingProperty or BindingElement,
// or when the parent is the whole Parameter.)
typeSuffix := binding.parent?.typeSuffix
continue unless typeSuffix
continue unless accessModifier or typeSuffix
// Remove accessModifier from parameter if we lift it to a field
if parameter.accessModifier
replaceNode parameter.accessModifier, undefined
parameter.accessModifier = undefined
id := binding.ref.id
continue if fields.has id
classExpressions.splice index++, 0, [fStatement[0], {
type: "FieldDefinition"
id
typeSuffix
children: [id, typeSuffix]
children: [accessModifier, id, typeSuffix]
}, ";"]
// Only the first definition gets an indent, stolen from fStatement
fStatement[0] = ""
Expand Down
3 changes: 2 additions & 1 deletion source/parser/types.civet
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ export type AtBinding =
ref: ASTRef
children: Children & [ASTRef]
parent?: Parent
names: []

export type BlockStatement =
type: "BlockStatement"
Expand Down Expand Up @@ -1199,7 +1200,7 @@ export type VoidType = ASTLeafWithType "VoidType"

export type TypeLiteralNode = ASTLeaf | VoidType

export type ThisAssignments = [string, ASTRef][]
export type ThisAssignments = ([string, ASTRef] | AssignmentExpression)[]

export type WithClause =
type: "WithClause"
Expand Down
22 changes: 22 additions & 0 deletions test/types/class.civet
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ describe "[TS] class", ->
}
"""

testCase """
constructor with readonly param
---
class Foo
@(readonly @var)
---
class Foo {
readonly var;constructor(_var){this.var = _var;}
}
"""

testCase """
const assignment becomes readonly field
---
Expand Down Expand Up @@ -467,6 +478,17 @@ describe "[TS] class", ->
}
"""

testCase """
with access modifiers
---
class UserAccount
@(public @name: string, readonly @var: number)
---
class UserAccount {
public name: string;readonly var: number;constructor(name: string, _var: number){this.name = name;this.var = _var;}
}
"""

testCase """
nested object fields
---
Expand Down

0 comments on commit abbf5dc

Please sign in to comment.