diff --git a/src/main/grammars/MoveParser.bnf b/src/main/grammars/MoveParser.bnf index 7613bf77..ee482d8e 100644 --- a/src/main/grammars/MoveParser.bnf +++ b/src/main/grammars/MoveParser.bnf @@ -290,6 +290,7 @@ Const ::= Attr* CONST_KW IDENTIFIER TypeAscription Initializer ';' implements = [ "org.move.lang.core.psi.MvQualNamedElement" "org.move.lang.core.psi.ext.MvItemElement" + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] mixin = "org.move.lang.core.psi.ext.MvConstMixin" stubClass = "org.move.lang.core.stubs.MvConstStub" @@ -521,6 +522,9 @@ private FunctionParameter_recover ::= !(')' | '{' | ';' | IDENTIFIER) FunctionParameter ::= PatBinding TypeAscription { pin = 1 + implements = [ + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" + ] mixin = "org.move.lang.core.psi.ext.MvFunctionParameterMixin" } @@ -589,6 +593,7 @@ NamedFieldDecl ::= Attr* IDENTIFIER TypeAscription &(',' | '}') "org.move.lang.core.psi.MvMandatoryNameIdentifierOwner" "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" "org.move.lang.core.psi.ext.MvFieldDecl" + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] mixin = "org.move.lang.core.psi.ext.MvNamedFieldDeclMixin" hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ] @@ -819,6 +824,9 @@ private SpecExprStmt_items ::= AssumeSpecExpr | AssertSpecExpr | AbortsIfSpecExp private post ::= <> fake LetStmt ::= let post? Pat? TypeAscription? Initializer? ';'? +{ + implements = [ "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] +} LetMoveStmt ::= let Pat TypeAscription? Initializer? ';' { @@ -1090,7 +1098,10 @@ upper TupleLitExprUpper ::= ',' [ Expr (',' Expr)* ','? ] ')' { LambdaExpr ::= LambdaParameterList Expr? { pin = 1 } LambdaParameterList ::= '|' !',' <>? '|' -LambdaParameter ::= PatBinding +LambdaParameter ::= PatBinding TypeAscription? +{ + implements = [ "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] +} RangeExpr ::= Expr '..' Expr { pin = 2 } @@ -1421,6 +1432,7 @@ ItemSpecFunctionParameter ::= IDENTIFIER TypeAscription { pin = 1 implements = [ "org.move.lang.core.resolve.ref.MvItemSpecParameterReferenceElement" + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] mixin = "org.move.lang.core.psi.ext.MvItemSpecFunctionParameterMixin" } @@ -1484,6 +1496,7 @@ fake SchemaFieldStmt ::= local? PatBinding TypeAscription ';' { implements = [ "org.move.lang.core.psi.MslOnlyElement" + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] } @@ -1505,6 +1518,7 @@ GlobalVariableStmt ::= Attr* global IDENTIFIER TypeParameterList? TypeAscription "org.move.lang.core.psi.MvNameIdentifierOwner" "org.move.lang.core.psi.MslOnlyElement" "org.move.lang.core.psi.ext.MvItemElement" + "org.move.lang.core.psi.ext.MvTypeAscriptionOwner" ] mixin = "org.move.lang.core.psi.ext.MvGlobalVariableMixin" } @@ -1756,7 +1770,7 @@ RangeQuantBinding ::= PatBinding in Expr { pin = 2 extends = QuantBinding } -TypeQuantBinding ::= PatBinding ':' Type { +TypeQuantBinding ::= PatBinding TypeAscription { pin = 2 extends = QuantBinding } diff --git a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt index bbf71a94..cc470128 100644 --- a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt +++ b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt @@ -27,16 +27,7 @@ import org.move.lang.core.psi.MvStruct import org.move.lang.core.psi.MvType import org.move.lang.core.psi.MvTypeParameter import org.move.lang.core.psi.MvTypeParameterList -import org.move.lang.core.psi.ext.MvDocAndAttributeOwner -import org.move.lang.core.psi.ext.abilityBounds -import org.move.lang.core.psi.ext.ancestorOrSelf -import org.move.lang.core.psi.ext.bindingOwner -import org.move.lang.core.psi.ext.fieldOwner -import org.move.lang.core.psi.ext.isMsl -import org.move.lang.core.psi.ext.isMslOnlyItem -import org.move.lang.core.psi.ext.isPhantom -import org.move.lang.core.psi.ext.itemElement -import org.move.lang.core.psi.ext.module +import org.move.lang.core.psi.ext.* import org.move.lang.core.psi.isNative import org.move.lang.core.psi.module import org.move.lang.core.types.infer.inference @@ -88,11 +79,11 @@ class MvDocumentationTarget(val element: PsiElement, private val originalElement fun generateDoc(element: PsiElement?): String? { val buffer = StringBuilder() + var docElement = element - if ( - docElement is MvPatBinding && docElement.bindingOwner is MvConst - ) - docElement = docElement.bindingOwner + if (docElement is MvPatBinding && docElement.bindingTypeOwner is MvConst) { + docElement = docElement.bindingTypeOwner + } when (docElement) { is MvNamedAddress -> { diff --git a/src/main/kotlin/org/move/ide/hints/type/MvTypeInlayHintsProvider2.kt b/src/main/kotlin/org/move/ide/hints/type/MvTypeInlayHintsProvider2.kt index 075f09c0..b1f87c94 100644 --- a/src/main/kotlin/org/move/ide/hints/type/MvTypeInlayHintsProvider2.kt +++ b/src/main/kotlin/org/move/ide/hints/type/MvTypeInlayHintsProvider2.kt @@ -5,18 +5,22 @@ import com.intellij.codeInsight.hints.declarative.HintFontSize.ABitSmallerThanIn import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import org.move.ide.presentation.hintText +import org.move.lang.core.psi.MvConst +import org.move.lang.core.psi.MvForIterCondition import org.move.lang.core.psi.MvFunctionParameter +import org.move.lang.core.psi.MvLambdaParameter import org.move.lang.core.psi.MvLetStmt +import org.move.lang.core.psi.MvPat import org.move.lang.core.psi.MvPatBinding +import org.move.lang.core.psi.MvPatField +import org.move.lang.core.psi.MvPatFieldFull +import org.move.lang.core.psi.MvRangeQuantBinding import org.move.lang.core.psi.MvSchemaFieldStmt -import org.move.lang.core.psi.ext.bindingOwner -import org.move.lang.core.psi.ext.endOffset -import org.move.lang.core.psi.ext.hasAncestor -import org.move.lang.core.psi.ext.isMsl -import org.move.lang.core.types.infer.inferenceOwner +import org.move.lang.core.psi.MvTypeQuantBinding +import org.move.lang.core.psi.ext.* import org.move.lang.core.types.infer.inference -import org.move.lang.core.types.ty.* +import org.move.lang.core.types.infer.inferenceOwner +import org.move.lang.core.types.ty.TyUnknown class MvTypeInlayHintsProvider2: InlayHintsProvider { @@ -33,12 +37,32 @@ class MvTypeInlayHintsProvider2: InlayHintsProvider { // skip private variables if (patBinding.name.startsWith("_")) return - // does not show hints for bindings with explicit type annotations - val owner = patBinding.bindingOwner - if (owner is MvFunctionParameter || owner is MvSchemaFieldStmt) return + val parent = patBinding.bindingTypeOwner + when (parent) { + // require explicit type annotations + is MvFunctionParameter, is MvConst, is MvSchemaFieldStmt, is MvTypeQuantBinding -> return + is MvLambdaParameter -> { + // if lambda parameter has explicit type + if (parent.type != null) return + } + is MvPatFieldFull -> { + // skip hints for `field: field_alias` + return + } + is MvLetStmt -> { + // explicit type for let stmt + if (parent.type != null) return + } + is MvPatField -> { + // field shorthand, show type hint + } + is MvForIterCondition, is MvRangeQuantBinding -> { + // show hints for iteration indexes + } + else -> return + } val contextInferenceOwner = patBinding.inferenceOwner() ?: return - val msl = patBinding.isMsl() val ty = contextInferenceOwner.inference(msl).getBindingType(patBinding) if (ty is TyUnknown) return diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt index 0b87238c..850e1be8 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnresolvedReferenceInspection.kt @@ -75,7 +75,8 @@ class MvUnresolvedReferenceInspection: MvLocalInspectionTool() { override fun visitSchemaLitField(field: MvSchemaLitField) { if (field.isShorthand) { val resolvedItems = field.reference.multiResolve() - val fieldBinding = resolvedItems.find { it is MvPatBinding && it.bindingOwner is MvSchemaFieldStmt } + val fieldBinding = resolvedItems + .find { it is MvPatBinding && it.bindingTypeOwner is MvSchemaFieldStmt } if (fieldBinding == null) { holder.registerProblem( field.referenceNameElement, diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnusedVariableInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnusedVariableInspection.kt index a035b174..86c65733 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnusedVariableInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnusedVariableInspection.kt @@ -6,12 +6,12 @@ import com.intellij.psi.util.descendantsOfType 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.bindingTypeOwner import org.move.lang.core.psi.ext.isMsl -import org.move.lang.core.psi.ext.bindingOwner -class MvUnusedVariableInspection : MvLocalInspectionTool() { +class MvUnusedVariableInspection: MvLocalInspectionTool() { override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = - object : MvVisitor() { + object: MvVisitor() { override fun visitLetStmt(o: MvLetStmt) { val bindings = o.pat?.descendantsOfType().orEmpty() for (binding in bindings) { @@ -37,7 +37,7 @@ class MvUnusedVariableInspection : MvLocalInspectionTool() { // filter out #[test] attributes .filter { it.element !is MvAttrItem } if (references.none()) { - val fixes = when (binding.bindingOwner) { + val fixes = when (binding.bindingTypeOwner) { is MvFunctionParameter -> arrayOf( RenameFix(binding, "_$bindingName"), RemoveParameterFix(binding, bindingName) diff --git a/src/main/kotlin/org/move/ide/presentation/PresentationInfo.kt b/src/main/kotlin/org/move/ide/presentation/PresentationInfo.kt index 2c0c08d6..46b30d4e 100644 --- a/src/main/kotlin/org/move/ide/presentation/PresentationInfo.kt +++ b/src/main/kotlin/org/move/ide/presentation/PresentationInfo.kt @@ -1,7 +1,7 @@ package org.move.ide.presentation import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.bindingOwner +import org.move.lang.core.psi.ext.bindingTypeOwner class PresentationInfo( val element: MvNamedElement, @@ -16,7 +16,7 @@ val MvNamedElement.presentationInfo: PresentationInfo? val type = when (this) { is MvTypeParameter -> "type parameter" is MvPatBinding -> { - val owner = this.bindingOwner + val owner = this.bindingTypeOwner when (owner) { is MvFunctionParameter -> "value parameter" is MvLetStmt -> "variable" diff --git a/src/main/kotlin/org/move/lang/MoveParserDefinition.kt b/src/main/kotlin/org/move/lang/MoveParserDefinition.kt index 42ed5bf6..f664db00 100644 --- a/src/main/kotlin/org/move/lang/MoveParserDefinition.kt +++ b/src/main/kotlin/org/move/lang/MoveParserDefinition.kt @@ -63,6 +63,6 @@ class MoveParserDefinition : ParserDefinition { /** * Should be increased after any change of parser rules */ - const val PARSER_VERSION: Int = LEXER_VERSION + 52 + const val PARSER_VERSION: Int = LEXER_VERSION + 53 } } diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt index 386ed099..c6cbe202 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt @@ -13,12 +13,23 @@ import org.move.lang.core.resolve2.ref.MvBindingPatReferenceImpl import org.move.lang.core.types.ty.Mutability import javax.swing.Icon +// todo: replace with bindingTypeOwner later val MvPatBinding.bindingOwner: PsiElement? get() = PsiTreeUtil.findFirstParent(this) { it is MvLetStmt || it is MvFunctionParameter || it is MvSchemaFieldStmt || it is MvLambdaParameter + || it is MvConst + } + +val MvPatBinding.bindingTypeOwner: PsiElement? + get() { + var owner = this.parent + if (owner is MvPat) { + owner = this.findFirstParent { it is MvTypeAscriptionOwner } + } + return owner } sealed class RsBindingModeKind { @@ -45,14 +56,14 @@ abstract class MvPatBindingMixin(node: ASTNode) : MvMandatoryNameIdentifierOwner override val referenceName: String get() = name override fun getIcon(flags: Int): Icon = - when (this.bindingOwner) { + when (this.bindingTypeOwner) { is MvFunctionParameter -> MoveIcons.PARAMETER is MvConst -> MoveIcons.CONST else -> MoveIcons.VARIABLE } override fun getUseScope(): SearchScope { - return when (this.bindingOwner) { + return when (this.bindingTypeOwner) { is MvFunctionParameter -> { val function = this.ancestorStrict() ?: return super.getUseScope() var combinedScope: SearchScope = LocalSearchScope(function) diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvTypeAscriptionOwner.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvTypeAscriptionOwner.kt new file mode 100644 index 00000000..a5369417 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvTypeAscriptionOwner.kt @@ -0,0 +1,8 @@ +package org.move.lang.core.psi.ext + +import org.move.lang.core.psi.MvElement +import org.move.lang.core.psi.MvType + +interface MvTypeAscriptionOwner: MvElement { + val type: MvType? +} \ No newline at end of file diff --git a/src/test/kotlin/org/move/ide/hints/InlayTypeHintsProvider2Test.kt b/src/test/kotlin/org/move/ide/hints/InlayTypeHintsProvider2Test.kt index 168f6cbb..529cc9a4 100644 --- a/src/test/kotlin/org/move/ide/hints/InlayTypeHintsProvider2Test.kt +++ b/src/test/kotlin/org/move/ide/hints/InlayTypeHintsProvider2Test.kt @@ -127,6 +127,47 @@ class InlayTypeHintsProvider2Test: DeclarativeInlayHintsProviderTestCase() { } """) + fun `test no inlay hint if explicit lambda param declared`() = checkByText(""" + module 0x1::m { + fun callback(elem: u8, ident: |u8|u8): u8 { ident(elem) } + fun main() { + callback(10, |elem: u8| elem + 1); + } + } + """) + + fun `test no inlay hint if explicit let type`() = checkByText(""" + module 0x1::m { + fun main() { + let a: u8 = 1; + } + } + """) + + fun `test no inlay hints for struct pattern destructor`() = checkByText(""" + module 0x1::m { + struct S { val: u8 } + fun main(s: S) { + let S { val: myval } = s; + } + } + """) + + fun `test inlay hints for struct pattern destructor shorthand`() = checkByText(""" + module 0x1::m { + struct S { val: u8 } + fun main(s: S) { + let S { val/*<# : |u8 #>*/ } = s; + } + } + """) + + fun `test no inlay hint for const`() = checkByText(""" + module 0x1::m { + const MY_CONST: u8 = 1; + } + """) + private fun checkByText(@Language("Move") code: String) { doTestProvider("main.move", code, MvTypeInlayHintsProvider2()) } diff --git a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt index 3590f569..2534387d 100644 --- a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt +++ b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt @@ -59,6 +59,7 @@ class CompleteParsingTest: MvParsingTestCase("complete") { fun `test loop invariants`() = doTest() fun `test loop labels`() = doTest() fun `test assign bin expr`() = doTest() + fun `test lambdas`() = doTest() fun doTest() { super.doTest(true, true) diff --git a/src/test/resources/org/move/lang/parser/complete/lambdas.move b/src/test/resources/org/move/lang/parser/complete/lambdas.move new file mode 100644 index 00000000..51e97052 --- /dev/null +++ b/src/test/resources/org/move/lang/parser/complete/lambdas.move @@ -0,0 +1,6 @@ +module 0x1::lambdas { + fun main() { + for_each(v, |f: &Function| {}); + for_each(v, |i: u8, g: u8| {}); + } +} diff --git a/src/test/resources/org/move/lang/parser/complete/lambdas.txt b/src/test/resources/org/move/lang/parser/complete/lambdas.txt new file mode 100644 index 00000000..16817cd4 --- /dev/null +++ b/src/test/resources/org/move/lang/parser/complete/lambdas.txt @@ -0,0 +1,104 @@ +FILE + MvModuleImpl(MODULE) + PsiElement(module)('module') + PsiWhiteSpace(' ') + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x1') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('lambdas') + PsiWhiteSpace(' ') + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('main') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvCallExprImpl(CALL_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('for_each') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvPathExprImpl(PATH_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLambdaExprImpl(LAMBDA_EXPR) + MvLambdaParameterListImpl(LAMBDA_PARAMETER_LIST) + PsiElement(|)('|') + MvLambdaParameterImpl(LAMBDA_PARAMETER) + MvPatBindingImpl(PAT_BINDING) + PsiElement(IDENTIFIER)('f') + PsiElement(:)(':') + PsiWhiteSpace(' ') + MvRefTypeImpl(REF_TYPE) + MvRefTypeStartImpl(REF_TYPE_START) + PsiElement(&)('&') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('Function') + PsiElement(|)('|') + PsiWhiteSpace(' ') + MvCodeBlockExprImpl(CODE_BLOCK_EXPR) + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiElement(})('}') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvCallExprImpl(CALL_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('for_each') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvPathExprImpl(PATH_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLambdaExprImpl(LAMBDA_EXPR) + MvLambdaParameterListImpl(LAMBDA_PARAMETER_LIST) + PsiElement(|)('|') + MvLambdaParameterImpl(LAMBDA_PARAMETER) + MvPatBindingImpl(PAT_BINDING) + PsiElement(IDENTIFIER)('i') + PsiElement(:)(':') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('u8') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvLambdaParameterImpl(LAMBDA_PARAMETER) + MvPatBindingImpl(PAT_BINDING) + PsiElement(IDENTIFIER)('g') + PsiElement(:)(':') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('u8') + PsiElement(|)('|') + PsiWhiteSpace(' ') + MvCodeBlockExprImpl(CODE_BLOCK_EXPR) + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiElement(})('}') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file