diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt index 850e1be8..69bf5a88 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt @@ -14,8 +14,6 @@ import org.move.lang.core.types.ty.TyUnknown class MvUnresolvedReferenceInspection: MvLocalInspectionTool() { -// var ignoreWithoutQuickFix: Boolean = false - override val isSyntaxOnly get() = false override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object: MvVisitor() { @@ -54,15 +52,19 @@ class MvUnresolvedReferenceInspection: MvLocalInspectionTool() { // checked in another method if (patField.patFieldFull != null) return + // pat struct unresolved, do not highlight fields + if (patField.patStruct.path.reference?.resolve() == null) return - patField.patBinding - ?.let { tryMultiResolveOrRegisterError(it, holder) } + patField.patBinding?.let { tryMultiResolveOrRegisterError(it, holder) } } - override fun visitPatFieldFull(o: MvPatFieldFull) { - if (o.isMsl() && !isDebugModeEnabled()) + override fun visitPatFieldFull(patFieldFull: MvPatFieldFull) { + if (patFieldFull.isMsl() && !isDebugModeEnabled()) return - tryMultiResolveOrRegisterError(o, holder) + // pat struct unresolved, do not highlight fields + if (patFieldFull.patStruct.path.reference?.resolve() == null) return + + tryMultiResolveOrRegisterError(patFieldFull, holder) } override fun visitStructLitField(litField: MvStructLitField) { diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/CommonCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/CommonCompletionProvider.kt index 878eaf9e..74fe25e6 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/CommonCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/CommonCompletionProvider.kt @@ -122,7 +122,7 @@ private fun skipAlreadyProvidedFields( val parent = refElement.parent val providedFieldNames = when (parent) { // shorthand, skip all provided fields - is MvPatField -> parent.parentPatStruct.fieldNames + is MvPatField -> parent.patStruct.fieldNames // is MvStructLitField -> parent.parentStructLitExpr.providedFieldNames else -> emptySet() } diff --git a/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt b/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt index ad4714fb..f4d4e156 100644 --- a/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt +++ b/src/main/kotlin/org/move/lang/core/completion/providers/StructFieldsCompletionProvider.kt @@ -40,7 +40,7 @@ object StructFieldsCompletionProvider: MvCompletionProvider() { val completionCtx = MvCompletionContext(element, element.isMsl()) when (element) { is MvPatField -> { - val patStruct = element.parentPatStruct + val patStruct = element.patStruct addFieldsToCompletion( patStruct.path.maybeStruct ?: return, patStruct.fieldNames, diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPat.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPat.kt index 89d10bda..323150f9 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPat.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPat.kt @@ -7,7 +7,7 @@ import org.move.lang.core.psi.MvPat import org.move.lang.core.psi.MvPatStruct -val MvPatField.parentPatStruct: MvPatStruct get() = ancestorStrict()!! +val MvPatField.patStruct: MvPatStruct get() = parent as MvPatStruct val MvPatField.fieldReferenceName: String get() = if (this.patFieldFull != null) { diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPatFull.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPatFull.kt index 1f268f44..a7a79617 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPatFull.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvFieldPatFull.kt @@ -2,17 +2,15 @@ package org.move.lang.core.psi.ext import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement -import org.move.lang.core.psi.MvElementImpl -import org.move.lang.core.psi.MvPatFieldFull -import org.move.lang.core.psi.MvNamedElement -import org.move.lang.core.psi.MvPatStruct +import org.move.lang.core.psi.* import org.move.lang.core.resolve.collectResolveVariants import org.move.lang.core.resolve.ref.MvPolyVariantReference import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached import org.move.lang.core.resolve.ref.ResolveCacheDependency import org.move.lang.core.resolve2.processStructPatFieldResolveVariants -val MvPatFieldFull.parentPatStruct: MvPatStruct get() = ancestorStrict()!! +val MvPatFieldFull.patField: MvPatField get() = parent as MvPatField +val MvPatFieldFull.patStruct: MvPatStruct get() = patField.patStruct abstract class MvPatFieldFullMixin(node: ASTNode): MvElementImpl(node), MvPatFieldFull { diff --git a/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt b/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt index 0860cc0d..f40726ad 100644 --- a/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt +++ b/src/main/kotlin/org/move/lang/core/resolve2/NameResolution2.kt @@ -63,10 +63,10 @@ fun processStructLitFieldResolveVariants( } fun processStructPatFieldResolveVariants( - field: MvPatFieldFull, + patFieldFull: MvPatFieldFull, processor: RsResolveProcessor ): Boolean { - val resolved = field.parentPatStruct.path.reference?.resolveFollowingAliases() + val resolved = patFieldFull.patStruct.path.reference?.resolveFollowingAliases() val resolvedStruct = resolved as? MvFieldsOwner ?: return false return processNamedFieldDeclarations(resolvedStruct, processor) } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt b/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt index 579535b5..21f2307d 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/InferenceContext.kt @@ -108,7 +108,6 @@ data class InferenceResult( fun getExpectedType(expr: MvExpr): Ty = exprExpectedTypes[expr] ?: TyUnknown fun getCallableType(callable: MvCallable): Ty? = callableTypes[callable] - fun hasResolvedPath(path: MvPath): Boolean = path in resolvedPaths fun getResolvedPath(path: MvPath): List? = resolvedPaths[path] ?: inferenceErrorOrFallback(path, null) @@ -120,7 +119,7 @@ data class InferenceResult( resolvedLitFields[litField].orEmpty() override fun getPatFieldType(patField: MvPatField): Ty = - patFieldTypes[patField] ?: TyUnknown + patFieldTypes[patField] ?: inferenceErrorOrFallback(patField, TyUnknown) } fun inferTypesIn(element: MvInferenceContextOwner, msl: Boolean): InferenceResult { @@ -332,15 +331,14 @@ class InferenceContext( // resolvedPaths[path] = resolved // } - override fun getPatFieldType(patField: MvPatField): Ty { - return patFieldTypes[patField] ?: TyUnknown - } + override fun getPatFieldType(patField: MvPatField): Ty = + patFieldTypes[patField] ?: inferenceErrorOrFallback(patField, TyUnknown) override fun getResolvedLitField(litField: MvStructLitField): List = resolvedLitFields[litField].orEmpty() fun getExprType(expr: MvExpr): Ty { - return exprTypes[expr] ?: TyUnknown + return exprTypes[expr] ?: inferenceErrorOrFallback(expr, TyUnknown) } fun combineTypes(ty1: Ty, ty2: Ty): RelateResult { diff --git a/src/main/kotlin/org/move/lang/core/types/infer/Patterns.kt b/src/main/kotlin/org/move/lang/core/types/infer/Patterns.kt index adf30bf9..e9bc2e50 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/Patterns.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/Patterns.kt @@ -45,18 +45,18 @@ fun MvPat.extractBindings(fcx: TypeInferenceWalker, ty: Ty, defBm: RsBindingMode val item = fcx.resolvePathCached(this.path, expected) as? MvFieldsOwner ?: (expected as? TyAdt)?.item as? MvStruct - ?: return if (item is MvGenericDeclaration) { val patTy = fcx.instantiatePath(this.path, item) ?: return -// val (patTy, _) = fcx.instantiateMethodOrPath(this.path, item) ?: return if (!isCompatible(expected, patTy, fcx.msl)) { fcx.reportTypeError(TypeError.InvalidUnpacking(this, ty)) } } - val structFields = item.namedFields.associateBy { it.name } + + val structFields = item?.namedFields?.associateBy { it.name } ?: emptyMap() for (fieldPat in this.patFieldList) { val kind = fieldPat.kind + // wil have TyUnknown on unresolved item val fieldType = structFields[kind.fieldName] ?.type ?.loweredType(fcx.msl) @@ -80,7 +80,10 @@ fun MvPat.extractBindings(fcx: TypeInferenceWalker, ty: Ty, defBm: RsBindingMode val item = fcx.resolvePathCached(this.path, expected) as? MvFieldsOwner ?: (expected as? TyAdt)?.item as? MvStruct - ?: return + if (item == null) { + patList.forEach { it.extractBindings(fcx, TyUnknown) } + return + } val tupleFields = item.positionalFields inferTupleFieldsTypes(fcx, patList, patBm, tupleFields.size) { indx -> tupleFields diff --git a/src/test/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspectionTest.kt index 687b0975..9afa0058 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspectionTest.kt @@ -541,4 +541,25 @@ module 0x1::m { } } """) + + @MoveV2 + fun `test no error for fields if destructuring unknown struct`() = checkByText(""" + module 0x1::m { + fun main() { + let S { val } = 1; + let S(val) = 1; + } + } + """) + + @MoveV2 + fun `test no error for fields if destructuring unknown tuple struct`() = checkByText(""" + module 0x1::m { + enum R {} + fun main() { + let R::Inner { val } = 1; + let R::Inner(val) = 1; + } + } + """) } diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt index 10dbddd3..fa0c9753 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt @@ -2221,4 +2221,45 @@ module 0x1::main { } } """) + + fun `test struct destructuring for unknown type`() = testExpr(""" + module 0x1::m { + fun main() { + let S { i } = s; + i; + //^ + } + } + """) + + fun `test enum variant destructuring for unknown type`() = testExpr(""" + module 0x1::m { + enum S { } + fun main() { + let S::Inner { i } = s; + i; + //^ + } + } + """) + + fun `test tuple struct destructuring for unknown type`() = testExpr(""" + module 0x1::m { + fun main() { + let S(i, j) = s; + i; + //^ + } + } + """) + fun `test tuple enum variant destructuring for unknown type`() = testExpr(""" + module 0x1::m { + enum S {} + fun main() { + let S::Inner(i, j) = s; + i; + //^ + } + } + """) }