Skip to content

Commit

Permalink
enum and match fields name resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurnikov committed Aug 5, 2024
1 parent 81397be commit be5b918
Show file tree
Hide file tree
Showing 7 changed files with 655 additions and 346 deletions.
7 changes: 5 additions & 2 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
tokenTypeClass="org.move.lang.core.MvTokenType"

extends(".*Expr")=Expr
extends(".*Pat")=Pat
extends("(Struct|Binding|Tuple|Wild|EnumVariant)Pat")=Pat
// extends(".*Pat")=Pat
extends("(Lambda|Ref|Path|Tuple|Unit|Parens)Type")=Type

elementType(".+BinExpr")=BinaryExpr
Expand Down Expand Up @@ -365,6 +366,7 @@ fake Struct ::= Attr* native? STRUCT_KW IDENTIFIER? TypeParameterList? Abilities
"org.move.lang.core.psi.MvQualNamedElement"
"org.move.lang.core.psi.MvTypeParametersOwner"
"org.move.lang.core.psi.ext.MvItemElement"
"org.move.lang.core.psi.ext.MvFieldsOwner"
]
mixin = "org.move.lang.core.psi.ext.MvStructMixin"
stubClass = "org.move.lang.core.stubs.MvStructStub"
Expand Down Expand Up @@ -410,6 +412,7 @@ EnumVariant ::= Attr* IDENTIFIER BlockFields?
implements = [
"org.move.lang.core.psi.MvNameIdentifierOwner"
"org.move.lang.core.psi.ext.MvDocAndAttributeOwner"
"org.move.lang.core.psi.ext.MvFieldsOwner"
]
mixin = "org.move.lang.core.psi.ext.MvEnumVariantMixin"
stubClass = "org.move.lang.core.stubs.MvEnumVariantStub"
Expand Down Expand Up @@ -683,7 +686,7 @@ BindingPat ::= IDENTIFIER !'::' {
TuplePat ::= '(' <<non_empty_comma_sep_items Pat>>? ')'

StructPat ::= PathImpl StructPatFieldsBlock
StructPatFieldsBlock ::= '{' StructPatField_with_recover* '}' { pin = 1 }
StructPatFieldsBlock ::= '{' StructPatField_with_recover* '}'

private StructPatField_with_recover ::= !'}' StructPatField (',' | &'}')
{
Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvFieldsOwner.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.move.lang.core.psi.ext

import org.move.lang.core.psi.MvBlockFields
import org.move.lang.core.psi.MvNameIdentifierOwner
import org.move.lang.core.psi.MvNamedFieldDecl

interface MvFieldsOwner: MvNameIdentifierOwner {
val blockFields: MvBlockFields?
}

val MvFieldsOwner.fields: List<MvNamedFieldDecl>
get() = namedFields //+ positionalFields

val MvFieldsOwner.namedFields: List<MvNamedFieldDecl>
get() = blockFields?.namedFieldDeclList.orEmpty()


/**
* True for:
* ```
* struct S;
* enum E { A }
* ```
* but false for:
* ```
* struct S {}
* struct S();
* ```
*/
val MvFieldsOwner.isFieldless: Boolean
get() = blockFields == null //&& tupleFields == null
4 changes: 3 additions & 1 deletion src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ fun MvPath.allowedNamespaces(): Set<Namespace> {
parentElement is MvCallExpr -> EnumSet.of(FUNCTION)
parentElement is MvRefExpr && this.hasAncestor<MvAttrItem>() -> EnumSet.of(NAME, MODULE)
parentElement is MvRefExpr -> EnumSet.of(NAME)
parentElement is MvStructLitExpr || parentElement is MvStructPat -> EnumSet.of(TYPE)
parentElement is MvStructLitExpr
|| parentElement is MvStructPat
|| parentElement is MvEnumVariantPat -> EnumSet.of(TYPE)
parentElement is MvAccessSpecifier -> EnumSet.of(TYPE)
parentElement is MvAddressSpecifierArg -> EnumSet.of(FUNCTION)
parentElement is MvAddressSpecifierCallParam -> EnumSet.of(NAME)
Expand Down
14 changes: 9 additions & 5 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,22 @@ fun processStructRefFieldResolveVariants(
fieldRef: MvStructRefField,
processor: RsResolveProcessor
): Boolean {
val structItem = fieldRef.maybeStruct ?: return false
return structItem.fields
val fieldsOwnerItem = fieldRef.fieldOwner ?: return false
return fieldsOwnerItem.fields
.any { field ->
processor.process(SimpleScopeEntry(field.name, field, setOf(Namespace.NAME)))
}
}

private val MvStructRefField.maybeStruct: MvStruct?
private val MvStructRefField.fieldOwner: MvFieldsOwner?
get() {
return when (this) {
is MvStructPatField -> this.structPat.structItem
is MvStructLitField -> this.structLitExpr.path.maybeStruct
is MvStructPatField -> {
this.structPat.path.reference?.resolveFollowingAliases() as? MvFieldsOwner
}
is MvStructLitField -> {
this.structLitExpr.path.reference?.resolveFollowingAliases() as? MvFieldsOwner
}
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ fun processItemsInScope(
false
}
}
is MvMatchArm -> {
if (cameFrom is MvMatchPat) continue
// only use those bindings for the match arm rhs
processor.processAll(scope.matchPat.pat.bindings.toList())
}
is MvItemSpec -> {
val item = scope.item
when (item) {
is MvFunction -> {
processor.processAll(
// contextScopeInfo,
item.valueParamsAsBindings,
item.specResultParameters.map { it.bindingPat },
)
Expand Down
233 changes: 233 additions & 0 deletions src/test/kotlin/org/move/lang/resolve/ResolveVariablesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -385,4 +385,237 @@ module 0x1::string_tests {
}
}
""")

fun `test resolve variable in match expr`() = checkByCode("""
module 0x1::m {
fun main() {
let m = 1;
//X
match (m) {
//^
}
}
}
""")

fun `test resolve function with match name`() = checkByCode("""
module 0x1::m {
fun match() {}
//X
fun main() {
match();
//^
}
}
""")


fun `test resolve type in match arm 1`() = checkByCode("""
module 0x1::m {
enum S { One, Two }
//X
fun main() {
let m = 1;
match (m) {
S::One => true
//^
S::Two => false
}
}
}
""")

fun `test resolve type in match arm 2`() = checkByCode("""
module 0x1::m {
enum S { One, Two }
//X
fun main() {
let m = 1;
match (m) {
S::One => true
//^
S::Two => false
}
}
}
""")

fun `test resolve type in match arm 3`() = checkByCode("""
module 0x1::m {
enum S { One, Two }
//X
fun main() {
let m = 1;
match (m) {
S::One => true
S::Two => false
//^
}
}
}
""")

fun `test resolve item in match arm body`() = checkByCode("""
module 0x1::m {
enum S { One, Two }
fun main() {
let m = 1;
//X
match (m) {
S::One => m
//^
}
}
}
""")

fun `test resolve fields for enum variant in match arm`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main() {
let m = 1;
match (m) {
S::One { field: f } => f
//^
}
}
}
""")

fun `test resolve shortcut field for enum variant in match arm`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main() {
let m = 1;
match (m) {
S::One { field } => field
//^
}
}
}
""")

fun `test resolve binding for field reassignment for enum variant`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
fun main() {
let m = 1;
match (m) {
S::One { field: myfield }
//X
=> myfield
//^
}
}
}
""")

fun `test resolve binding for shortcut field for enum variant`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
fun main() {
let m = 1;
match (m) {
S::One { field }
//X
=> field
//^
}
}
}
""")

fun `test resolve field for struct pat in enum`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let S::One { field } = s;
//^
}
}
""")

fun `test resolve field reassignment for struct pat in enum`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let S::One { field: f } = s;
//^
}
}
""")

fun `test resolve field reassignment for struct pat in enum binding`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
fun main(s: S::One) {
let S::One { field: f } = s;
//X
f;
//^
}
}
""")

fun `test resolve enum variant for struct lit`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let f = 1;
let s = S::One { field: f };
//^
}
}
""")

fun `test resolve enum variant for struct pat`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let S::One { field } = s;
//^
}
}
""")

fun `test resolve enum variant for struct pat 2`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let S::One { field } = s;
//^
}
}
""")

fun `test resolve field reassignment for struct lit enum variant`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
//X
fun main(s: S::One) {
let f = 1;
let s = S::One { field: f };
//^
}
}
""")

fun `test resolve field reassignment for struct lit enum variant binding`() = checkByCode("""
module 0x1::m {
enum S { One { field: u8 }, Two }
fun main(s: S::One) {
let f = 1;
//X
let s = S::One { field: f };
//^
}
}
""")
}
Loading

0 comments on commit be5b918

Please sign in to comment.