diff --git a/src/main/kotlin/org/move/ide/hints/CallInfo.kt b/src/main/kotlin/org/move/ide/hints/CallInfo.kt index a7c68673e..1bbf7632c 100644 --- a/src/main/kotlin/org/move/ide/hints/CallInfo.kt +++ b/src/main/kotlin/org/move/ide/hints/CallInfo.kt @@ -23,10 +23,10 @@ class CallInfo( companion object { fun resolve(callExpr: MvCallExpr): CallInfo? { - val fn = callExpr.path.reference?.resolveFollowingAliases() as? MvFunction ?: return null val msl = callExpr.isMsl() val callTy = callExpr.inference(msl)?.getCallableType(callExpr) as? TyFunction ?: return null - return buildFunctionParameters(fn, callTy) + val item = callExpr.path.reference?.resolveFollowingAliases() ?: return null + return buildFunctionParameters(item, callTy) } fun resolve(assertMacroExpr: MvAssertMacroExpr): CallInfo { @@ -43,14 +43,24 @@ class CallInfo( return buildFunctionParameters(fn, callTy) } - private fun buildFunctionParameters(function: MvFunction, ty: TyFunction): CallInfo { - val tys = ty.paramTypes.drop(if (function.isMethod) 1 else 0) - val params = function.parameters - .drop(if (function.isMethod) 1 else 0).map { it.name to it.type } - val self = function.selfParam?.let { - ty.paramTypes.firstOrNull()?.let { "self: ${tyToString(it)}" } ?: "_" + private fun buildFunctionParameters(item: MvElement, ty: TyFunction): CallInfo? { + return when (item) { + is MvFunction -> { + val tys = ty.paramTypes.drop(if (item.isMethod) 1 else 0) + val params = item.parameters + .drop(if (item.isMethod) 1 else 0).map { it.name to it.type } + val self = item.selfParam?.let { + ty.paramTypes.firstOrNull()?.let { "self: ${tyToString(it)}" } ?: "_" + } + CallInfo(self, buildParameters(tys, params)) + } + is MvStruct, is MvEnumVariant -> { + // tuple struct + val tys = ty.paramTypes + CallInfo(null, tys.map { Parameter(null, null, it) }) + } + else -> null } - return CallInfo(self, buildParameters(tys, params)) } private fun buildParameters( diff --git a/src/main/kotlin/org/move/ide/hints/paramInfo/CompositeParameterInfoHandler.kt b/src/main/kotlin/org/move/ide/hints/paramInfo/CompositeParameterInfoHandler.kt index d0a25e5bd..de7e23999 100644 --- a/src/main/kotlin/org/move/ide/hints/paramInfo/CompositeParameterInfoHandler.kt +++ b/src/main/kotlin/org/move/ide/hints/paramInfo/CompositeParameterInfoHandler.kt @@ -47,10 +47,6 @@ class CompositeParameterInfoHandler: ParameterInfoHandler lowerPath(methodOrPath, namedItem.enumItem, msl) + is MvEnumVariant -> { + // has to be MvPath of form `ENUM_NAME::ENUM_VARIANT_NAME` + val enumPath = (methodOrPath as? MvPath)?.qualifier ?: return TyUnknown + lowerPath(enumPath, namedItem.enumItem, msl) + } else -> debugErrorOrFallback( "${namedItem.elementType} path cannot be inferred into type", TyUnknown @@ -84,16 +88,17 @@ class TyLowering { fun lowerCallable( methodOrPath: MvMethodOrPath, - item: MvGenericDeclaration, + namedItem: MvGenericDeclaration, parameterTypes: List, returnType: Ty, msl: Boolean ): TyFunction { - val typeParamsSubst = item.typeParamsToTypeParamsSubst - val acqTypes = (item as? MvFunctionLike)?.acquiresPathTypes.orEmpty().map { it.loweredType(msl) } - val baseTy = TyFunction(item, typeParamsSubst, parameterTypes, returnType, acqTypes) + val acqTypes = (namedItem as? MvFunctionLike)?.acquiresPathTypes.orEmpty().map { it.loweredType(msl) } + val typeParamsSubst = namedItem.typeParamsToTypeParamsSubst + + val baseTy = TyFunction(namedItem, typeParamsSubst, parameterTypes, returnType, acqTypes) - val explicitTypeParams = explicitTypeParamsSubst(methodOrPath, item, msl) + val explicitTypeParams = explicitTypeParamsSubst(methodOrPath, namedItem, msl) return baseTy.substitute(explicitTypeParams) as TyFunction } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index f78a55cc1..433ac13dd 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -324,7 +324,7 @@ class TypeInferenceWalker( TyUnknown } } - is MvEnumVariant -> item.enumItem.declaredType(ctx.msl) + is MvEnumVariant -> TyLowering().lowerPath(pathExpr.path, item, ctx.msl) is MvModule -> TyUnknown else -> debugErrorOrFallback( "Referenced item ${item.elementType} " + @@ -403,8 +403,7 @@ class TypeInferenceWalker( is MvFieldsOwner -> { val tupleFields = namedItem.tupleFields if (tupleFields == null) return TyUnknown - val itemTy = instantiateTupleCallable(tupleFields, path, namedItem) - itemTy + instantiateTupleCallable(tupleFields, path, namedItem) ?: return TyUnknown } is MvPatBinding -> { ctx.getBindingType(namedItem) as? TyLambda @@ -594,34 +593,24 @@ class TypeInferenceWalker( private fun instantiateTupleCallable( tupleFields: MvTupleFields, - methodOrPath: MvMethodOrPath, + path: MvPath, item: MvFieldsOwner - ): TyFunction { - val genericItem = when (item) { - is MvStruct -> item - is MvEnumVariant -> item.enumItem + ): TyFunction? { + val (genericItem, genericPath) = when (item) { + is MvStruct -> item to path + is MvEnumVariant -> { + val qualifierPath = path.qualifier ?: return null + item.enumItem to qualifierPath + } else -> error("exhaustive") } -// val parameterTypes = tupleFields.tupleFieldDeclList.map { it.type.loweredType(msl) } val returnType = genericItem.declaredType(msl) + val baseFuncTy = + TyLowering().lowerCallable(genericPath, genericItem, parameterTypes, returnType, msl) - val funcTy = TyLowering().lowerCallable(methodOrPath, genericItem, parameterTypes, returnType, msl) -// -// val baseTy = TyFunction( -// genericItem, -// genericItem.typeParamsToTypeParamsSubst, -// paramTypes = parameterTypes, -// returnType = returnType, -// acquiresTypes = emptyList(), -// ) -// -// val explicitTypeParamsSubst = TyLowering().explicitTypeParamsSubst(methodOrPath, genericItem, msl) -// val funcTy = baseTy.substitute(explicitTypeParamsSubst) - -// val baseTy = TyLowering().lowerTupleCallable(methodOrPath, item, tupleFields, msl) val tyVarsSubst = genericItem.tyVarsSubst - return funcTy.substitute(tyVarsSubst) as TyFunction + return baseFuncTy.substitute(tyVarsSubst) as TyFunction } fun inferMacroCallExprTy(macroExpr: MvAssertMacroExpr): Ty { diff --git a/src/main/kotlin/org/move/lang/core/types/ty/TyFunction.kt b/src/main/kotlin/org/move/lang/core/types/ty/TyFunction.kt index 59104ad07..087620249 100644 --- a/src/main/kotlin/org/move/lang/core/types/ty/TyFunction.kt +++ b/src/main/kotlin/org/move/lang/core/types/ty/TyFunction.kt @@ -60,19 +60,19 @@ data class TyFunction( } } -// do not account for explicit type arguments +// does not account for explicit type arguments fun MvFunctionLike.functionTy(msl: Boolean): TyFunction = rawFunctionTy(this, msl) private fun rawFunctionTy(item: MvFunctionLike, msl: Boolean): TyFunction { val typeParamsSubst = item.typeParamsToTypeParamsSubst val paramTypes = item.parameters.map { it.type?.loweredType(msl) ?: TyUnknown } - val acquiredTypes = item.acquiresPathTypes.map { it.loweredType(msl) } + val acqTypes = item.acquiresPathTypes.map { it.loweredType(msl) } val retType = item.returnTypeTy(msl) return TyFunction( item, typeParamsSubst, paramTypes, retType, - acquiredTypes, + acqTypes, ) } diff --git a/src/test/kotlin/org/move/ide/hints/ParameterInfoHandlerTest.kt b/src/test/kotlin/org/move/ide/hints/ParameterInfoHandlerTest.kt index c94766ed7..e00d7319c 100644 --- a/src/test/kotlin/org/move/ide/hints/ParameterInfoHandlerTest.kt +++ b/src/test/kotlin/org/move/ide/hints/ParameterInfoHandlerTest.kt @@ -205,4 +205,44 @@ class ParameterInfoHandlerTest: } """, "_: bool, err: u64", 0 ) + + @MoveV2 + fun `test parameter info for named tuple struct`() = checkByText(""" + module 0x1::m { + struct S(u8, u16); + fun main() { + S(/*caret*/); + } + } + """, "u8, u16", 0) + + @MoveV2 + fun `test parameter info for generic named tuple struct`() = checkByText(""" + module 0x1::m { + struct S(T, T); + fun main() { + S(/*caret*/); + } + } + """, "u8, u8", 0) + + @MoveV2 + fun `test parameter info for named tuple enum variant`() = checkByText(""" + module 0x1::m { + enum S { One(u8, u8) } + fun main() { + S::One(/*caret*/); + } + } + """, "u8, u8", 0) + + @MoveV2 + fun `test parameter info for generic named tuple enum variant`() = checkByText(""" + module 0x1::m { + enum S { One(T, T) } + fun main() { + S::One(/*caret*/); + } + } + """, "u8, u8", 0) } diff --git a/src/test/kotlin/org/move/lang/types/CallableTypeTest.kt b/src/test/kotlin/org/move/lang/types/CallableTypeTest.kt index cae1e3f7b..1a9d115f3 100644 --- a/src/test/kotlin/org/move/lang/types/CallableTypeTest.kt +++ b/src/test/kotlin/org/move/lang/types/CallableTypeTest.kt @@ -2,6 +2,7 @@ package org.move.lang.types import org.intellij.lang.annotations.Language import org.move.ide.presentation.text +import org.move.lang.core.psi.MvCallExpr import org.move.lang.core.psi.MvMethodCall import org.move.lang.core.psi.ext.MvCallable import org.move.lang.core.psi.ext.isMsl @@ -54,6 +55,17 @@ class CallableTypeTest: TypificationTestCase() { } """) + fun `test function with explicit integer parameter callable type`() = testFunctionType(""" + module 0x1::m { + fun call(t: T): T { t } + fun main() { + call(1); + //^ fn(u8) -> u8 + } + } + """) + + private fun testFunctionType(@Language("Move") code: String) = testCallableType(code) private fun testMethodType(@Language("Move") code: String) = testCallableType(code) private inline fun testCallableType(@Language("Move") code: String) { diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt index 01ebb5526..10dbddd32 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt @@ -1,5 +1,6 @@ package org.move.lang.types +import org.move.utils.tests.MoveV2 import org.move.utils.tests.NamedAddress import org.move.utils.tests.types.TypificationTestCase @@ -2185,4 +2186,39 @@ module 0x1::main { } } """) + + @MoveV2 + fun `test generic enum variant`() = testExpr(""" + module 0x1::m { + enum S { One } + fun main() { + let a = S::One; + a; + //^ 0x1::m::S + } + } + """) + + @MoveV2 + fun `test generic enum variant with positional fields`() = testExpr(""" + module 0x1::m { + enum S { One(T, T) } + fun main() { + let a = S::One(1, 1); + a; + //^ 0x1::m::S + } + } + """) + + fun `test function with explicit integer parameter return type`() = testExpr(""" + module 0x1::m { + fun call(t: T): T { t } + fun main() { + let a = call(1); + a; + //^ u8 + } + } + """) }