Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ".." pattern support #189

Merged
merged 6 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/grammars/MoveLexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ IDENTIFIER=[_a-zA-Z][_a-zA-Z0-9]*
";" { return SEMICOLON; }
"," { return COMMA; }
"." { return DOT; }
".." { return DOT_DOT; }
"=" { return EQ; }
"==" { return EQ_EQ; }
"!=" { return NOT_EQ; }
Expand Down
17 changes: 10 additions & 7 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
tokenTypeClass="org.move.lang.core.MvTokenType"

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

elementType(".+BinExpr")=BinaryExpr
Expand Down Expand Up @@ -729,6 +729,7 @@ TypeArgument ::= Type
// | ConstPat

Pat ::= PatWild
| PatRest
| PatTuple
| PatBinding
| PatStruct
Expand All @@ -742,6 +743,8 @@ EnumVariantPat ::= PathImpl
PatWild ::= '_'
WildPat ::= '_'

PatRest ::= '..'

ConstPat ::= PathImpl
PatConst ::= PathExpr

Expand All @@ -766,7 +769,7 @@ PatBinding ::= IDENTIFIER !ForbiddenPatBindingLast {
BindingPat ::= IDENTIFIER !ForbiddenPatBindingLast

//private ForbiddenBindingPatLast ::= '...' | '::' | '..=' | '..' | '<' | '(' | '{' | '!' {
private ForbiddenPatBindingLast ::= '::' | '<' | '(' | '{' | '!'
private ForbiddenPatBindingLast ::= '::' | '..' | '<' | '(' | '{' | '!'
{
consumeTokenMethod = "consumeTokenFast"
}
Expand All @@ -775,7 +778,7 @@ TuplePat ::= '(' PatParenListElem_with_recover* ')'
PatTuple ::= '(' PatParenListElem_with_recover* ')'

StructPat ::= PathImpl '{' PatField_with_recover* '}'
PatStruct ::= PathImpl '{' PatField_with_recover* '}'
PatStruct ::= PathImpl '{' PatField_with_recover* PatRest? '}'
{
implements = [ "org.move.lang.core.resolve2.ref.InferenceCachedPathElement" ]
}
Expand All @@ -790,13 +793,13 @@ private PatParenListElem_with_recover ::= !')' Pat (',' | &')') {
}
private PatParenListElem_recover ::= !(')' | Pat_first)

private PatField_with_recover ::= !'}' PatField (',' | &'}')
private PatField_with_recover ::= !('}' | '..') PatField (',' | &'}')
{
pin = 1
recoverWhile = PatField_recover

}
private PatField_recover ::= !('}' | IDENTIFIER)
private PatField_recover ::= !('}' | '..' | IDENTIFIER)

PatFieldFull ::= IDENTIFIER ':' Pat
{
Expand All @@ -821,7 +824,7 @@ FieldPat ::= PatFieldFull | PatBinding
//}
FieldPatBinding ::= ':' Pat

private Pat_first ::= '_' | '(' | Path_first | AnyLitToken_first
private Pat_first ::= '_' | '..' | '(' | Path_first | AnyLitToken_first

///////////////////////////////////////////////////////////////////////////////////////////////////
// Stmts
Expand Down Expand Up @@ -1101,7 +1104,7 @@ upper TupleLitExprUpper ::= ',' [ Expr (',' Expr)* ','? ] ')' {

LambdaExpr ::= '|' <<non_empty_comma_sep_items PatBinding>> '|' Expr { pin = 1 }

RangeExpr ::= Expr dotdot Expr { pin = 2 }
RangeExpr ::= Expr '..' Expr { pin = 2 }


private HEX_INTEGER_LITERAL ::= <<hexIntegerLiteral>>
Expand Down
40 changes: 31 additions & 9 deletions src/main/kotlin/org/move/ide/annotator/MvErrorAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,19 @@ class MvErrorAnnotator: MvAnnotatorBase() {
}
}

override fun visitPatStruct(o: MvPatStruct) {
val nameElement = o.path.referenceNameElement ?: return
val refStruct = o.path.maybeStruct ?: return
checkMissingFields(
moveHolder, nameElement, o.fieldNames, refStruct
)
override fun visitPatStruct(patStruct: MvPatStruct) {
val declaration =
patStruct.path.reference?.resolveFollowingAliases() as? MvFieldsOwner ?: return
val bodyFieldNames = patStruct.fieldNames
val missingFields = declaration.namedFields.filter { it.name !in bodyFieldNames }
if (missingFields.isNotEmpty() && patStruct.patRest == null) {
Diagnostic.MissingFieldsInStructPattern(patStruct, declaration, missingFields)
.addToHolder(moveHolder)
}
}

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
Expand Down Expand Up @@ -353,15 +358,32 @@ 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(
holder: MvAnnotationHolder,
target: PsiElement,
providedFieldNames: Set<String>,
referredStruct: MvStruct,
bodyFieldNames: Set<String>,
declaration: MvFieldsOwner,
) {
if ((referredStruct.fieldNames.toSet() - providedFieldNames).isNotEmpty()) {
if ((declaration.fieldNames.toSet() - bodyFieldNames).isNotEmpty()) {
holder.createErrorAnnotation(target, "Some fields are missing")
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/move/ide/docs/MvDocumentationProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ class MvDocumentationProvider : AbstractDocumentationProvider() {
val buffer = StringBuilder()
var docElement = element
if (
docElement is MvPatBinding && docElement.owner is MvConst
docElement is MvPatBinding && docElement.bindingOwner is MvConst
)
docElement = docElement.owner
docElement = docElement.bindingOwner

when (docElement) {
is MvNamedAddress -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class MvUnresolvedReferenceInspection: MvLocalInspectionTool() {
override fun visitSchemaLitField(field: MvSchemaLitField) {
if (field.isShorthand) {
val resolvedItems = field.reference.multiResolve()
val fieldBinding = resolvedItems.find { it is MvPatBinding && it.owner is MvSchemaFieldStmt }
val fieldBinding = resolvedItems.find { it is MvPatBinding && it.bindingOwner is MvSchemaFieldStmt }
if (fieldBinding == null) {
holder.registerProblem(
field.referenceNameElement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.move.ide.inspections.fixes.RemoveParameterFix
import org.move.ide.inspections.fixes.RenameFix
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.isMsl
import org.move.lang.core.psi.ext.owner
import org.move.lang.core.psi.ext.bindingOwner

class MvUnusedVariableInspection : MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
Expand Down Expand Up @@ -37,7 +37,7 @@ class MvUnusedVariableInspection : MvLocalInspectionTool() {
// filter out #[test] attributes
.filter { it.element !is MvAttrItem }
if (references.none()) {
val fixes = when (binding.owner) {
val fixes = when (binding.bindingOwner) {
is MvFunctionParameter -> arrayOf(
RenameFix(binding, "_$bindingName"),
RemoveParameterFix(binding, bindingName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import org.move.ide.inspections.fixes.RemoveTestSignerFix
import org.move.lang.core.psi.MvAttr
import org.move.lang.core.psi.MvAttrItem
import org.move.lang.core.psi.MvVisitor
import org.move.lang.core.psi.ext.isTest
import org.move.lang.core.psi.ext.unqualifiedName

class UnusedTestSignerInspection: MvLocalInspectionTool() {
override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor =
Expand All @@ -14,11 +16,11 @@ class UnusedTestSignerInspection: MvLocalInspectionTool() {
// stop if not a top-level item
if (attrItem.parent !is MvAttr) return
// stop if not a #[test]
if (attrItem.referenceName != "test") return
if (!attrItem.isTest) return

val innerAttrItems = attrItem.attrItemList?.attrItemList.orEmpty()
for (innerAttrItem in innerAttrItems) {
val refName = innerAttrItem.referenceName ?: continue
val refName = innerAttrItem.unqualifiedName ?: continue
if (innerAttrItem.unresolved) {
holder.registerProblem(
innerAttrItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.intellij.psi.util.parentOfType
import org.move.ide.inspections.DiagnosticFix
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.testAttrItem
import org.move.lang.core.psi.ext.unqualifiedName
import org.move.lang.core.psi.ext.valueArguments

/**
Expand Down Expand Up @@ -54,7 +55,7 @@ private fun removeTestSignerAssignment(function: MvFunction, signerParameterName
val attrItemList = testAttrItem.attrItemList
if (attrItemList != null) {
val signerAssigment =
attrItemList.attrItemList.find { it.identifier.text == signerParameterName }
attrItemList.attrItemList.find { it.unqualifiedName == signerParameterName }
signerAssigment?.deleteWithSurroundingCommaAndWhitespace()
if (attrItemList.attrItemList.isEmpty()) {
attrItemList.delete()
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/move/ide/presentation/PresentationInfo.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.move.ide.presentation

import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.owner
import org.move.lang.core.psi.ext.bindingOwner

class PresentationInfo(
val element: MvNamedElement,
Expand All @@ -16,7 +16,7 @@ val MvNamedElement.presentationInfo: PresentationInfo?
val type = when (this) {
is MvTypeParameter -> "type parameter"
is MvPatBinding -> {
val owner = this.owner
val owner = this.bindingOwner
when (owner) {
is MvFunctionParameter -> "value parameter"
is MvLetStmt -> "variable"
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/move/ide/refactoring/MvRenameProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.intellij.usageView.UsageInfo
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.equalsTo
import org.move.lang.core.psi.ext.isShorthand
import org.move.lang.core.psi.ext.owner
import org.move.lang.core.psi.ext.bindingOwner


class MvRenameProcessor: RenamePsiElementProcessor() {
Expand Down Expand Up @@ -50,7 +50,7 @@ class MvRenameProcessor: RenamePsiElementProcessor() {
}
}
is MvPatBinding -> {
val owner = element.owner
val owner = element.bindingOwner
usages.forEach {
when (owner) {
is MvSchemaFieldStmt -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.move.lang.MvElementTypes.IDENTIFIER
import org.move.lang.core.psi.MvPatBinding
import org.move.lang.core.psi.MvLetStmt
import org.move.lang.core.psi.ext.elementType
import org.move.lang.core.psi.ext.owner
import org.move.lang.core.psi.ext.bindingOwner

class MvCompletionConfidence : CompletionConfidence() {
override fun shouldSkipAutopopup(contextElement: PsiElement, psiFile: PsiFile, offset: Int): ThreeState {
Expand All @@ -17,7 +17,7 @@ class MvCompletionConfidence : CompletionConfidence() {
// (`let Foo { ... }`), so we show the completion popup in this case
if (contextElement.elementType == IDENTIFIER) {
val parent = contextElement.parent
if (parent is MvPatBinding && parent.owner is MvLetStmt) {
if (parent is MvPatBinding && parent.bindingOwner is MvLetStmt) {
val identText = contextElement.node.chars
if (identText.firstOrNull()?.isLowerCase() == true) {
return ThreeState.YES
Expand Down
3 changes: 1 addition & 2 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvAttr.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ package org.move.lang.core.psi.ext

import org.move.lang.core.psi.MvAttr

val MvAttr.owner: MvDocAndAttributeOwner?
get() = this.parent as? MvDocAndAttributeOwner
val MvAttr.attributeOwner: MvDocAndAttributeOwner? get() = this.parent as? MvDocAndAttributeOwner
7 changes: 5 additions & 2 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvAttrItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import org.move.lang.core.psi.impl.MvNamedElementImpl
import org.move.lang.core.resolve.ref.MvPolyVariantReference
import org.move.lang.core.resolve.ref.MvPolyVariantReferenceCached

val MvAttrItem.unqualifiedName: String get() = this.identifier.text

val MvAttrItem.attr: MvAttr? get() = this.parent as? MvAttr
val MvAttrItem.innerAttrItems: List<MvAttrItem> get() = this.attrItemList?.attrItemList.orEmpty()

val MvAttrItem.isAbortCode: Boolean get() = this.identifier.textMatches("abort_code")
val MvAttrItem.isTest: Boolean get() = this.identifier.textMatches("test")

class AttrItemReferenceImpl(
element: MvAttrItem,
Expand All @@ -30,8 +33,8 @@ abstract class MvAttrItemMixin(node: ASTNode): MvNamedElementImpl(node),
val attr = this.ancestorStrict<MvAttr>() ?: return null
attr.attrItemList
.singleOrNull()
?.takeIf { it.identifier.text == "test" } ?: return null
val ownerFunction = attr.owner as? MvFunction ?: return null
?.takeIf { it.isTest } ?: return null
val ownerFunction = attr.attributeOwner as? MvFunction ?: return null
return AttrItemReferenceImpl(this, ownerFunction)
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.move.lang.core.resolve2.ref.MvBindingPatReferenceImpl
import org.move.lang.core.types.ty.Mutability
import javax.swing.Icon

val MvPatBinding.owner: PsiElement?
val MvPatBinding.bindingOwner: PsiElement?
get() = PsiTreeUtil.findFirstParent(this) {
it is MvLetStmt
|| it is MvFunctionParameter
Expand Down Expand Up @@ -44,14 +44,14 @@ abstract class MvPatBindingMixin(node: ASTNode) : MvMandatoryNameIdentifierOwner
override val referenceName: String get() = name

override fun getIcon(flags: Int): Icon =
when (this.owner) {
when (this.bindingOwner) {
is MvFunctionParameter -> MoveIcons.PARAMETER
is MvConst -> MoveIcons.CONST
else -> MoveIcons.VARIABLE
}

override fun getUseScope(): SearchScope {
return when (this.owner) {
return when (this.bindingOwner) {
is MvFunctionParameter -> {
val function = this.ancestorStrict<MvFunction>() ?: return super.getUseScope()
var combinedScope: SearchScope = LocalSearchScope(function)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.move.lang.core.psi.MvAttrItem
import org.move.lang.core.psi.MvElement
import org.move.lang.core.stubs.MvAttributeOwnerStub

interface MvDocAndAttributeOwner : MvElement, NavigatablePsiElement {
interface MvDocAndAttributeOwner: MvElement, NavigatablePsiElement {
val attrList: List<MvAttr>

fun docComments(): Sequence<PsiElement> {
Expand Down Expand Up @@ -63,10 +63,8 @@ class QueryAttributes(
val isVerifyOnly: Boolean get() = hasAttrItem("verify_only")

fun hasAttrItem(attributeName: String): Boolean = getAttrItem(attributeName) != null

fun getAttrItem(attributeName: String): MvAttrItem? {
return this.attrItems.find { it.referenceName == attributeName }
}
fun getAttrItem(attributeName: String): MvAttrItem? =
this.attrItems.find { it.unqualifiedName == attributeName }

val attrItems: Sequence<MvAttrItem> get() = this.attributes.flatMap { it.attrItemList }

Expand Down
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/main/kotlin/org/move/lang/core/psi/ext/MvRefExpr.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ fun MvPathExpr.isAbortCodeConst(): Boolean {
?.takeIf { it.isAbortCode }
?: return false
val attr = abortCodeItem.ancestorStrict<MvAttr>() ?: return false
return (attr.owner as? MvFunction)?.hasTestAttr ?: false
return (attr.attributeOwner as? MvFunction)?.hasTestAttr ?: false
}
Loading
Loading