diff --git a/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt b/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt
index 44439718..25c2dd58 100644
--- a/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt
+++ b/src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt
@@ -160,6 +160,8 @@ class MvErrorAnnotator: MvAnnotatorBase() {
}
}
+ override fun visitPatTupleStruct(o: MvPatTupleStruct) = checkPatTupleStruct(moveHolder, o)
+
override fun visitStructLitExpr(o: MvStructLitExpr) {
val nameElement = o.path.referenceNameElement ?: return
val struct = o.path.maybeStruct ?: return
@@ -356,6 +358,23 @@ class MvErrorAnnotator: MvAnnotatorBase() {
}
}
}
+
+ private fun checkPatTupleStruct(holder: MvAnnotationHolder, patTupleStruct: MvPatTupleStruct) {
+ val declaration = patTupleStruct.path.reference?.resolveFollowingAliases() as? MvFieldsOwner ?: return
+
+ val declarationFieldsAmount = declaration.fields.size
+ // Rest is non-binding, meaning it is accepted even if all fields are already bound
+ val bodyFieldsAmount = patTupleStruct.patList.filterNot { it is MvPatRest }.size
+
+ if (bodyFieldsAmount < declarationFieldsAmount && patTupleStruct.patRest == null) {
+ Diagnostic.MissingFieldsInTuplePattern(
+ patTupleStruct,
+ declaration,
+ declarationFieldsAmount,
+ bodyFieldsAmount
+ ).addToHolder(holder)
+ }
+ }
}
private fun checkMissingFields(
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvPatTupleStruct.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvPatTupleStruct.kt
new file mode 100644
index 00000000..50b5eff5
--- /dev/null
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvPatTupleStruct.kt
@@ -0,0 +1,6 @@
+package org.move.lang.core.psi.ext
+
+import org.move.lang.core.psi.MvPatRest
+import org.move.lang.core.psi.MvPatTupleStruct
+
+val MvPatTupleStruct.patRest: MvPatRest? get() = patList.firstOrNull { it is MvPatRest } as? MvPatRest
\ No newline at end of file
diff --git a/src/main/kotlin/org/move/lang/utils/Diagnostic.kt b/src/main/kotlin/org/move/lang/utils/Diagnostic.kt
index 417cf019..e9305f38 100644
--- a/src/main/kotlin/org/move/lang/utils/Diagnostic.kt
+++ b/src/main/kotlin/org/move/lang/utils/Diagnostic.kt
@@ -230,6 +230,23 @@ sealed class Diagnostic(
}
}
+ class MissingFieldsInTuplePattern(
+ pat: MvPat,
+ private val declaration: MvFieldsOwner,
+ private val expectedAmount: Int,
+ private val actualAmount: Int
+ ): Diagnostic(pat) {
+
+ override fun prepare(): PreparedAnnotation {
+ val itemType = if (declaration is MvEnumVariant) "Enum variant" else "Tuple struct"
+ return PreparedAnnotation(
+ ERROR,
+ "$itemType pattern does not correspond to its declaration: " +
+ "expected $expectedAmount ${pluralize("field", expectedAmount)}, found $actualAmount"
+ )
+ }
+ }
+
class MissingFieldsInStructPattern(
patStruct: MvPatStruct,
private val declaration: MvFieldsOwner,
diff --git a/src/test/kotlin/org/move/ide/annotator/errors/StructFieldsNumberErrorTest.kt b/src/test/kotlin/org/move/ide/annotator/errors/StructFieldsNumberErrorTest.kt
index 0331ccd5..a37ac4ca 100644
--- a/src/test/kotlin/org/move/ide/annotator/errors/StructFieldsNumberErrorTest.kt
+++ b/src/test/kotlin/org/move/ide/annotator/errors/StructFieldsNumberErrorTest.kt
@@ -53,4 +53,30 @@ class StructFieldsNumberErrorTest: AnnotatorTestCase(MvErrorAnnotator::class) {
}
""")
+ fun `test missing positional fields for struct`() = checkErrors("""
+ module 0x1::m {
+ struct S(u8, u8);
+ fun main(s: S) {
+ let S (val) = s;
+ }
+ }
+ """)
+
+ fun `test missing positional fields with rest`() = checkErrors("""
+ module 0x1::m {
+ struct S(u8, u8);
+ fun main(s: S) {
+ let S(val, ..) = s;
+ }
+ }
+ """)
+
+ fun `test missing positional fields for enum variant`() = checkErrors("""
+ module 0x1::m {
+ enum S { Inner(u8, u8) }
+ fun main(s: S) {
+ let S::Inner(val) = s;
+ }
+ }
+ """)
}