@@ -3,13 +3,23 @@ import * as lua from "../../LuaAST";
33import { transformBuiltinPropertyAccessExpression } from "../builtins" ;
44import { FunctionVisitor , TransformationContext } from "../context" ;
55import { AnnotationKind , getTypeAnnotations } from "../utils/annotations" ;
6- import { annotationRemoved , invalidMultiReturnAccess } from "../utils/diagnostics" ;
6+ import {
7+ annotationRemoved ,
8+ invalidMultiReturnAccess ,
9+ unsupportedOptionalCompileMembersOnly ,
10+ } from "../utils/diagnostics" ;
711import { addToNumericExpression } from "../utils/lua-ast" ;
812import { LuaLibFeature , transformLuaLibFunction } from "../utils/lualib" ;
913import { isArrayType , isNumberType , isStringType } from "../utils/typescript" ;
1014import { tryGetConstEnumValue } from "./enum" ;
1115import { transformOrderedExpressions } from "./expression-list" ;
1216import { isMultiReturnCall , returnsMultiType } from "./language-extensions/multi" ;
17+ import {
18+ transformOptionalChainWithCapture ,
19+ ExpressionWithThisValue ,
20+ isOptionalContinuation ,
21+ captureThisValue ,
22+ } from "./optional-chaining" ;
1323
1424function addOneToArrayAccessArgument (
1525 context : TransformationContext ,
@@ -32,18 +42,31 @@ export function transformElementAccessArgument(
3242 return addOneToArrayAccessArgument ( context , node , index ) ;
3343}
3444
35- export const transformElementAccessExpression : FunctionVisitor < ts . ElementAccessExpression > = ( node , context ) => {
45+ export const transformElementAccessExpression : FunctionVisitor < ts . ElementAccessExpression > = ( node , context ) =>
46+ transformElementAccessExpressionWithCapture ( context , node , undefined ) . expression ;
47+ export function transformElementAccessExpressionWithCapture (
48+ context : TransformationContext ,
49+ node : ts . ElementAccessExpression ,
50+ thisValueCapture : lua . Identifier | undefined
51+ ) : ExpressionWithThisValue {
3652 const constEnumValue = tryGetConstEnumValue ( context , node ) ;
3753 if ( constEnumValue ) {
38- return constEnumValue ;
54+ return { expression : constEnumValue } ;
55+ }
56+
57+ if ( ts . isOptionalChain ( node ) ) {
58+ return transformOptionalChainWithCapture ( context , node , thisValueCapture ) ;
3959 }
4060
4161 const [ table , accessExpression ] = transformOrderedExpressions ( context , [ node . expression , node . argumentExpression ] ) ;
4262
4363 const type = context . checker . getTypeAtLocation ( node . expression ) ;
4464 const argumentType = context . checker . getTypeAtLocation ( node . argumentExpression ) ;
4565 if ( isStringType ( context , type ) && isNumberType ( context , argumentType ) ) {
46- return transformLuaLibFunction ( context , LuaLibFeature . StringAccess , node , table , accessExpression ) ;
66+ // strings are not callable, so ignore thisValueCapture
67+ return {
68+ expression : transformLuaLibFunction ( context , LuaLibFeature . StringAccess , node , table , accessExpression ) ,
69+ } ;
4770 }
4871
4972 const updatedAccessExpression = addOneToArrayAccessArgument ( context , node , accessExpression ) ;
@@ -56,30 +79,33 @@ export const transformElementAccessExpression: FunctionVisitor<ts.ElementAccessE
5679
5780 // When selecting the first element, we can shortcut
5881 if ( ts . isNumericLiteral ( node . argumentExpression ) && node . argumentExpression . text === "0" ) {
59- return table ;
82+ return { expression : table } ;
6083 } else {
6184 const selectIdentifier = lua . createIdentifier ( "select" ) ;
62- const selectCall = lua . createCallExpression ( selectIdentifier , [ updatedAccessExpression , table ] ) ;
63- return selectCall ;
85+ return { expression : lua . createCallExpression ( selectIdentifier , [ updatedAccessExpression , table ] ) } ;
6486 }
6587 }
6688
67- if ( ts . isOptionalChain ( node ) ) {
68- return transformLuaLibFunction (
69- context ,
70- LuaLibFeature . OptionalChainAccess ,
71- node ,
72- table ,
73- updatedAccessExpression
74- ) ;
89+ if ( thisValueCapture ) {
90+ const thisValue = captureThisValue ( context , table , thisValueCapture , node . expression ) ;
91+ return {
92+ expression : lua . createTableIndexExpression ( thisValue , updatedAccessExpression , node ) ,
93+ thisValue,
94+ } ;
7595 }
96+ return { expression : lua . createTableIndexExpression ( table , updatedAccessExpression , node ) } ;
97+ }
7698
77- return lua . createTableIndexExpression ( table , updatedAccessExpression , node ) ;
78- } ;
79-
80- export const transformPropertyAccessExpression : FunctionVisitor < ts . PropertyAccessExpression > = ( node , context ) => {
99+ export const transformPropertyAccessExpression : FunctionVisitor < ts . PropertyAccessExpression > = ( node , context ) =>
100+ transformPropertyAccessExpressionWithCapture ( context , node , undefined ) . expression ;
101+ export function transformPropertyAccessExpressionWithCapture (
102+ context : TransformationContext ,
103+ node : ts . PropertyAccessExpression ,
104+ thisValueCapture : lua . Identifier | undefined
105+ ) : ExpressionWithThisValue {
81106 const property = node . name . text ;
82107 const type = context . checker . getTypeAtLocation ( node . expression ) ;
108+ const isOptionalLeft = isOptionalContinuation ( node . expression ) ;
83109
84110 const annotations = getTypeAnnotations ( type ) ;
85111
@@ -89,49 +115,54 @@ export const transformPropertyAccessExpression: FunctionVisitor<ts.PropertyAcces
89115
90116 const constEnumValue = tryGetConstEnumValue ( context , node ) ;
91117 if ( constEnumValue ) {
92- return constEnumValue ;
93- }
94-
95- const builtinResult = transformBuiltinPropertyAccessExpression ( context , node ) ;
96- if ( builtinResult ) {
97- return builtinResult ;
118+ return { expression : constEnumValue } ;
98119 }
99120
100121 if ( ts . isCallExpression ( node . expression ) && returnsMultiType ( context , node . expression ) ) {
101122 context . diagnostics . push ( invalidMultiReturnAccess ( node ) ) ;
102123 }
103124
125+ if ( ts . isOptionalChain ( node ) ) {
126+ return transformOptionalChainWithCapture ( context , node , thisValueCapture ) ;
127+ }
128+
104129 // Do not output path for member only enums
105130 if ( annotations . has ( AnnotationKind . CompileMembersOnly ) ) {
131+ if ( isOptionalLeft ) {
132+ context . diagnostics . push ( unsupportedOptionalCompileMembersOnly ( node ) ) ;
133+ }
106134 if ( ts . isPropertyAccessExpression ( node . expression ) ) {
107135 // in case of ...x.enum.y transform to ...x.y
108- return lua . createTableIndexExpression (
136+ const expression = lua . createTableIndexExpression (
109137 context . transformExpression ( node . expression . expression ) ,
110138 lua . createStringLiteral ( property ) ,
111139 node
112140 ) ;
141+ return { expression } ;
113142 } else {
114- return lua . createIdentifier ( property , node ) ;
143+ return { expression : lua . createIdentifier ( property , node ) } ;
115144 }
116145 }
117146
118- if ( ts . isOptionalChain ( node ) ) {
119- // Only handle full optional chains separately, not partial ones
120- return transformOptionalChain ( context , node ) ;
147+ const builtinResult = transformBuiltinPropertyAccessExpression ( context , node ) ;
148+ if ( builtinResult ) {
149+ // Ignore thisValueCapture.
150+ // This assumes that nothing returned by builtin property accesses are callable.
151+ // If this assumption is no longer true, this may need to be updated.
152+ return { expression : builtinResult } ;
121153 }
122154
123- const callPath = context . transformExpression ( node . expression ) ;
124- return lua . createTableIndexExpression ( callPath , lua . createStringLiteral ( property ) , node ) ;
125- } ;
126-
127- function transformOptionalChain (
128- context : TransformationContext ,
129- node : ts . OptionalChain & ts . PropertyAccessExpression
130- ) : lua . CallExpression {
131- const left = context . transformExpression ( node . expression ) ;
132- const right = lua . createStringLiteral ( node . name . text , node . name ) ;
155+ const table = context . transformExpression ( node . expression ) ;
133156
134- return transformLuaLibFunction ( context , LuaLibFeature . OptionalChainAccess , node , left , right ) ;
157+ if ( thisValueCapture ) {
158+ const thisValue = captureThisValue ( context , table , thisValueCapture , node . expression ) ;
159+ const expression = lua . createTableIndexExpression ( thisValue , lua . createStringLiteral ( property ) , node ) ;
160+ return {
161+ expression,
162+ thisValue,
163+ } ;
164+ }
165+ return { expression : lua . createTableIndexExpression ( table , lua . createStringLiteral ( property ) , node ) } ;
135166}
136167
137168export const transformQualifiedName : FunctionVisitor < ts . QualifiedName > = ( node , context ) => {
0 commit comments