From 821bb5a86204f03b36318188a689132c4c9fe20c Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 30 Jun 2024 16:50:50 +0300 Subject: [PATCH 01/10] add parser test --- .../move/lang/parser/CompleteParsingTest.kt | 1 + .../move/lang/parser/complete/index_expr.move | 45 + .../move/lang/parser/complete/index_expr.txt | 822 ++++++++++++++++++ 3 files changed, 868 insertions(+) create mode 100644 src/test/resources/org/move/lang/parser/complete/index_expr.move create mode 100644 src/test/resources/org/move/lang/parser/complete/index_expr.txt diff --git a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt index 09dd9af41..5af0fc569 100644 --- a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt +++ b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt @@ -45,6 +45,7 @@ class CompleteParsingTest : MvParsingTestCase("complete") { @EnableResourceAccessControl fun `test access control`() = doTest() + fun `test index expr`() = doTest() fun doTest() { super.doTest(true, true) diff --git a/src/test/resources/org/move/lang/parser/complete/index_expr.move b/src/test/resources/org/move/lang/parser/complete/index_expr.move new file mode 100644 index 000000000..34e4f8cc3 --- /dev/null +++ b/src/test/resources/org/move/lang/parser/complete/index_expr.move @@ -0,0 +1,45 @@ +module 0x1::index_expr { + struct X has copy, drop, store { + value: M + } + + struct Y has key, drop { + field: T + } + + fun test_vector() { + let v = vector[x, x]; + assert!(v[0].value == 2, 0); + } + + fun test_vector_borrow_mut() { + let v = vector[y1, y2]; + (&mut v[0]).field.value = false; + (&mut v[1]).field.value = true; + assert!((&v[0]).field.value == false, 0); + assert!((&v[1]).field.value == true, 0); + } + + fun test_resource_3() acquires R { + use 0x42::test; + assert!((&test::Y>[@0x1]).field.value == true, 0); + } + + fun test_resource_4() acquires R { + let addr = @0x1; + let y = &mut 0x42::test::Y> [addr]; + y.field.value = false; + spec { + assert Y>[addr].field.value == false; + }; + assert!((&Y>[addr]).field.value == false, 1); + } + + fun test_resource_5() acquires Y { + let addr = @0x1; + let y = &mut 0x42::test::Y> [addr]; + y.field.value = false; + let y_resource = Y>[addr]; + assert!(y_resource.field.value == false, 1); + } +} diff --git a/src/test/resources/org/move/lang/parser/complete/index_expr.txt b/src/test/resources/org/move/lang/parser/complete/index_expr.txt new file mode 100644 index 000000000..30fff8a0e --- /dev/null +++ b/src/test/resources/org/move/lang/parser/complete/index_expr.txt @@ -0,0 +1,822 @@ +FILE + MvModuleImpl(MODULE) + PsiElement(module)('module') + PsiWhiteSpace(' ') + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x1') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('index_expr') + PsiWhiteSpace(' ') + MvModuleBlockImpl(MODULE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvStructImpl(STRUCT) + PsiElement(struct_kw)('struct') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('X') + MvTypeParameterListImpl(TYPE_PARAMETER_LIST) + PsiElement(<)('<') + MvTypeParameterImpl(TYPE_PARAMETER) + PsiElement(IDENTIFIER)('M') + PsiElement(>)('>') + PsiWhiteSpace(' ') + MvAbilitiesListImpl(ABILITIES_LIST) + PsiElement(has_kw)('has') + PsiWhiteSpace(' ') + MvAbilityImpl(ABILITY) + PsiElement(copy)('copy') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvAbilityImpl(ABILITY) + PsiElement(IDENTIFIER)('drop') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvAbilityImpl(ABILITY) + PsiElement(IDENTIFIER)('store') + PsiWhiteSpace(' ') + MvStructBlockImpl(STRUCT_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvStructFieldImpl(STRUCT_FIELD) + PsiElement(IDENTIFIER)('value') + MvTypeAnnotationImpl(TYPE_ANNOTATION) + PsiElement(:)(':') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('M') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvStructImpl(STRUCT) + PsiElement(struct_kw)('struct') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Y') + MvTypeParameterListImpl(TYPE_PARAMETER_LIST) + PsiElement(<)('<') + MvTypeParameterImpl(TYPE_PARAMETER) + PsiElement(IDENTIFIER)('T') + PsiElement(>)('>') + PsiWhiteSpace(' ') + MvAbilitiesListImpl(ABILITIES_LIST) + PsiElement(has_kw)('has') + PsiWhiteSpace(' ') + MvAbilityImpl(ABILITY) + PsiElement(IDENTIFIER)('key') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvAbilityImpl(ABILITY) + PsiElement(IDENTIFIER)('drop') + PsiWhiteSpace(' ') + MvStructBlockImpl(STRUCT_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvStructFieldImpl(STRUCT_FIELD) + PsiElement(IDENTIFIER)('field') + MvTypeAnnotationImpl(TYPE_ANNOTATION) + PsiElement(:)(':') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('T') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test_vector') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('v') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvVectorLitExprImpl(VECTOR_LIT_EXPR) + PsiElement(IDENTIFIER)('vector') + MvVectorLitItemsImpl(VECTOR_LIT_ITEMS) + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('x') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('x') + PsiElement(])(']') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement(])(']') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('2') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test_vector_borrow_mut') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('v') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvVectorLitExprImpl(VECTOR_LIT_EXPR) + PsiElement(IDENTIFIER)('vector') + MvVectorLitItemsImpl(VECTOR_LIT_ITEMS) + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('y1') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('y2') + PsiElement(])(']') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssignmentExprImpl(ASSIGNMENT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + PsiElement(mut)('mut') + PsiWhiteSpace(' ') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssignmentExprImpl(ASSIGNMENT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + PsiElement(mut)('mut') + PsiWhiteSpace(' ') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('true') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('v') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('true') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test_resource_3') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvAcquiresTypeImpl(ACQUIRES_TYPE) + PsiElement(acquires)('acquires') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('R') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvUseStmtImpl(USE_STMT) + PsiElement(use)('use') + PsiWhiteSpace(' ') + MvModuleUseSpeckImpl(MODULE_USE_SPECK) + MvFQModuleRefImpl(FQ_MODULE_REF) + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x42') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('test') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + MvModuleRefImpl(MODULE_REF) + PsiElement(IDENTIFIER)('test') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiElement([)('[') + MvLitExprImpl(LIT_EXPR) + MvAddressLitImpl(ADDRESS_LIT) + PsiElement(@)('@') + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x1') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('true') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('0') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test_resource_4') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvAcquiresTypeImpl(ACQUIRES_TYPE) + PsiElement(acquires)('acquires') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('R') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('addr') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + MvAddressLitImpl(ADDRESS_LIT) + PsiElement(@)('@') + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x1') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('y') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + PsiElement(mut)('mut') + PsiWhiteSpace(' ') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + MvFQModuleRefImpl(FQ_MODULE_REF) + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x42') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('test') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiWhiteSpace(' ') + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('addr') + PsiElement(])(']') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssignmentExprImpl(ASSIGNMENT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('y') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvItemSpecBlockExprImpl(ITEM_SPEC_BLOCK_EXPR) + PsiElement(spec)('spec') + PsiWhiteSpace(' ') + MvSpecCodeBlockImpl(SPEC_CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvSpecExprStmtImpl(SPEC_EXPR_STMT) + MvAssertSpecExprImpl(ASSERT_SPEC_EXPR) + PsiElement(assert_kw)('assert') + PsiWhiteSpace(' ') + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('addr') + PsiElement(])(']') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvParensExprImpl(PARENS_EXPR) + PsiElement(()('(') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('addr') + PsiElement(])(']') + PsiElement())(')') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n\n ') + MvFunctionImpl(FUNCTION) + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test_resource_5') + MvFunctionParameterListImpl(FUNCTION_PARAMETER_LIST) + PsiElement(()('(') + PsiElement())(')') + PsiWhiteSpace(' ') + MvAcquiresTypeImpl(ACQUIRES_TYPE) + PsiElement(acquires)('acquires') + PsiWhiteSpace(' ') + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('Y') + PsiWhiteSpace(' ') + MvCodeBlockImpl(CODE_BLOCK) + PsiElement({)('{') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('addr') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + MvAddressLitImpl(ADDRESS_LIT) + PsiElement(@)('@') + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x1') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('y') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvBorrowExprImpl(BORROW_EXPR) + PsiElement(&)('&') + PsiElement(mut)('mut') + PsiWhiteSpace(' ') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + MvFQModuleRefImpl(FQ_MODULE_REF) + MvAddressRefImpl(ADDRESS_REF) + PsiElement(DIEM_ADDRESS)('0x42') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('test') + PsiElement(::)('::') + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiWhiteSpace(' ') + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('addr') + PsiElement(])(']') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssignmentExprImpl(ASSIGNMENT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('y') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvLetStmtImpl(LET_STMT) + PsiElement(let)('let') + PsiWhiteSpace(' ') + MvBindingPatImpl(BINDING_PAT) + PsiElement(IDENTIFIER)('y_resource') + PsiWhiteSpace(' ') + MvInitializerImpl(INITIALIZER) + PsiElement(=)('=') + PsiWhiteSpace(' ') + MvIndexExprImpl(INDEX_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('Y') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('X') + MvTypeArgumentListImpl(TYPE_ARGUMENT_LIST) + PsiElement(<)('<') + MvTypeArgumentImpl(TYPE_ARGUMENT) + MvPathTypeImpl(PATH_TYPE) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('bool') + PsiElement(>)('>') + PsiElement(>)('>') + PsiElement([)('[') + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('addr') + PsiElement(])(']') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + MvExprStmtImpl(EXPR_STMT) + MvAssertBangExprImpl(ASSERT_BANG_EXPR) + PsiElement(IDENTIFIER)('assert') + PsiElement(!)('!') + MvValueArgumentListImpl(VALUE_ARGUMENT_LIST) + PsiElement(()('(') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvBinaryExprImpl(BINARY_EXPR[==]) + MvDotExprImpl(DOT_EXPR) + MvDotExprImpl(DOT_EXPR) + MvRefExprImpl(REF_EXPR) + MvPathImpl(PATH) + PsiElement(IDENTIFIER)('y_resource') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('field') + PsiElement(.)('.') + MvStructDotFieldImpl(STRUCT_DOT_FIELD) + PsiElement(IDENTIFIER)('value') + PsiWhiteSpace(' ') + MvBinaryOpImpl(BINARY_OP) + PsiElement(==)('==') + PsiWhiteSpace(' ') + MvLitExprImpl(LIT_EXPR) + PsiElement(BOOL_LITERAL)('false') + PsiElement(,)(',') + PsiWhiteSpace(' ') + MvValueArgumentImpl(VALUE_ARGUMENT) + MvLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiElement())(')') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + PsiElement(})('}') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file From 7969fff5888c70c7fc9db8b97274471c1fff38f9 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 30 Jun 2024 17:56:41 +0300 Subject: [PATCH 02/10] vector index expr --- .../org/move/lang/core/psi/ext/MvExpr.kt | 2 + .../move/lang/core/types/infer/TypeError.kt | 13 ++ .../core/types/infer/TypeInferenceWalker.kt | 24 ++- .../inspections/MvTypeCheckInspectionTest.kt | 122 ++++++++---- .../lang/types/ExpressionTypeInferenceTest.kt | 24 +++ .../move/lang/types/ExpressionTypesTest.kt | 180 +++++++++++++++++- 6 files changed, 319 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvExpr.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvExpr.kt index 9d58c1193..fddda25f7 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvExpr.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvExpr.kt @@ -17,6 +17,8 @@ val MvExpr.isAtomExpr: Boolean get() = val MvIndexExpr.receiverExpr: MvExpr get() = exprList.first() +val MvIndexExpr.argExpr: MvExpr get() = exprList.drop(1).first() + val MvExpr.declaration: MvElement? get() = when (this) { is PathExpr -> path.reference?.resolve() diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt index f91c11e25..63201e4ba 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt @@ -165,4 +165,17 @@ sealed class TypeError(open val element: PsiElement) : TypeFoldable { return InvalidDereference(element, folder.fold(actualTy)) } } + + data class IndexingIsNotAllowed( + override val element: PsiElement, + val actualTy: Ty, + ): TypeError(element) { + override fun message(): String { + return "Indexing receiver type should be vector or resource, got '${actualTy.text(fq = false)}'" + } + + override fun innerFoldWith(folder: TypeFolder): TypeError { + return IndexingIsNotAllowed(element, folder.fold(actualTy)) + } + } } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index 3d6c10038..0474b83b8 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -656,15 +656,21 @@ class TypeInferenceWalker( } private fun inferIndexExprTy(indexExpr: MvIndexExpr): Ty { - val receiverExpr = indexExpr.exprList.first() - val receiverTy = receiverExpr.inferTypeCoercableTo(TyVector(TyUnknown)) - - val posExpr = indexExpr.exprList.drop(1).first() - val posTy = posExpr.inferType() - return when (posTy) { - is TyRange -> (receiverTy as? TyVector) ?: TyUnknown - is TyNum -> (receiverTy as? TyVector)?.item ?: TyUnknown - else -> TyUnknown + val receiverTy = indexExpr.receiverExpr.inferType() + val argTy = indexExpr.argExpr.inferType() + return when (receiverTy) { + is TyVector -> { + // argExpr can be either TyInteger or TyRange + when (argTy) { + is TyRange -> receiverTy + is TyInteger, is TyInfer.IntVar, is TyNum -> receiverTy.item + else -> TyUnknown + } + } + else -> { + ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) + TyUnknown + } } } diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index 6c8952770..612d94c69 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -2,7 +2,7 @@ package org.move.ide.inspections import org.move.utils.tests.annotation.InspectionTestBase -class MvTypeCheckInspectionTest : InspectionTestBase(MvTypeCheckInspection::class) { +class MvTypeCheckInspectionTest: InspectionTestBase(MvTypeCheckInspection::class) { fun `test incorrect type address passed where &signer is expected`() = checkErrors( """ module 0x1::M { @@ -1322,32 +1322,39 @@ module 0x1::pool { """ ) - fun `test incompatible integers to gte`() = checkByText(""" + fun `test incompatible integers to gte`() = checkByText( + """ module 0x1::m { fun main() { 1u8 >= 1u64; } } - """) + """ + ) - fun `test bit shift requires u8`() = checkByText(""" + fun `test bit shift requires u8`() = checkByText( + """ module 0x1::m { fun main() { 1 << 1000u64; } } - """) + """ + ) - fun `test type check incomplete call expr get correct param`() = checkByText(""" + fun `test type check incomplete call expr get correct param`() = checkByText( + """ module 0x1::m { fun call(a: u64, b: u8) {} fun main() { call(, 2u64); } } - """) + """ + ) - fun `test if else last expr returns incorrect type`() = checkByText(""" + fun `test if else last expr returns incorrect type`() = checkByText( + """ module 0x1::m { fun main() { let my_vol_ref = 1u64; @@ -1361,9 +1368,11 @@ module 0x1::pool { }; } } - """) + """ + ) - fun `test if else last expr only if returns incorrect type`() = checkByText(""" + fun `test if else last expr only if returns incorrect type`() = checkByText( + """ module 0x1::m { fun main() { let my_vol_ref = 1u64; @@ -1377,9 +1386,11 @@ module 0x1::pool { }; } } - """) + """ + ) - fun `test if else inline returns incorrect type`() = checkByText(""" + fun `test if else inline returns incorrect type`() = checkByText( + """ module 0x1::m { fun main() { let my_vol_ref = 1u64; @@ -1391,9 +1402,11 @@ module 0x1::pool { ; } } - """) + """ + ) - fun `test if else inline if returns incorrect type`() = checkByText(""" + fun `test if else inline if returns incorrect type`() = checkByText( + """ module 0x1::m { fun main() { let my_vol_ref = 1u64; @@ -1405,9 +1418,11 @@ module 0x1::pool { ; } } - """) + """ + ) - fun `test block returns incorrect type of integer error on last expression`() = checkByText(""" + fun `test block returns incorrect type of integer error on last expression`() = checkByText( + """ module 0x1::m { fun main() { let my_vol_ref = 1u64; @@ -1417,9 +1432,11 @@ module 0x1::pool { }; } } - """) + """ + ) - fun `test unpack mut ref with tuple pattern`() = checkByText(""" + fun `test unpack mut ref with tuple pattern`() = checkByText( + """ module 0x1::m { struct Bin has store { reserves_x: u64, @@ -1430,75 +1447,110 @@ module 0x1::pool { let (a, b) = bin; } } - """) + """ + ) - fun `test cannot reference another reference`() = checkByText(""" + fun `test cannot reference another reference`() = checkByText( + """ module 0x1::m { struct Pool {} fun main(pool: &mut Pool) { &pool; } } - """) + """ + ) - fun `test cannot reference tuple`() = checkByText(""" + fun `test cannot reference tuple`() = checkByText( + """ module 0x1::m { fun call(): (u8, u8) { (1, 1) } fun main() { &call(); } } - """) + """ + ) - fun `test cannot dereference non reference type`() = checkByText(""" + fun `test cannot dereference non reference type`() = checkByText( + """ module 0x1::m { fun main(pool: &mut Pool) { *1; } } - """) + """ + ) - fun `test range expr second has different type`() = checkByText(""" + fun `test range expr second has different type`() = checkByText( + """ module 0x1::m { fun main() { let a = 1..true; } } - """) + """ + ) - fun `test return type inference for generic expr left`() = checkByText(""" + fun `test return type inference for generic expr left`() = checkByText( + """ module 0x1::m { native fun borrow(): Value; fun main() { borrow() + 3; } } - """) + """ + ) - fun `test return type inference for deref borrow expr left`() = checkByText(""" + fun `test return type inference for deref borrow expr left`() = checkByText( + """ module 0x1::m { native fun borrow(): &Value; fun main() { *borrow() + 3; } } - """) + """ + ) - fun `test return type inference for deref borrow expr right`() = checkByText(""" + fun `test return type inference for deref borrow expr right`() = checkByText( + """ module 0x1::m { native fun borrow(): &Value; fun main() { 3 + *borrow(); } } - """) + """ + ) - fun `test unpacking of struct reference allowed`() = checkByText(""" + fun `test unpacking of struct reference allowed`() = checkByText( + """ module 0x1::m { struct Field { id: u8 } fun main() { let Field { id } = &Field { id: 1 }; } } - """) + """ + ) + + fun `test error receiver of vector index expr`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[0]; + } + } + """) + + fun `test error receiver of resource index expr`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[@0x1]; + } + } + """) } diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt index 055e50d31..099fffe60 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt @@ -561,4 +561,28 @@ module 0x1::main { } } """) + + fun `test integer type inference with vector index expr 1`() = testExpr(""" + module 0x1::m { + fun main() { + let a = 1; + let v = vector[2u8]; + a == v[0]; + a; + //^ u8 + } + } + """) + + fun `test integer type inference with vector index expr 2`() = testExpr(""" + module 0x1::m { + fun main() { + let a = 1u8; + let v = vector[2]; + a == v[0]; + v; + //^ vector + } + } + """) } diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt index a499bf10d..ac2b463c9 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt @@ -1199,8 +1199,8 @@ module 0x1::main { """ module 0x1::m { spec module { - let v = vector[false]; - let a = v[1]; + let v = vector[false]; + let a = v[0]; a; //^ bool } @@ -1834,4 +1834,180 @@ module 0x1::main { } } """) + + fun `test vector index expr`() = testExpr(""" + module 0x1::m { + fun test_vector() { + let v = vector[true, true]; + (v[0]); + //^ bool + } + } + """) + + fun `test vector index expr u8`() = testExpr(""" + module 0x1::m { + fun test_vector() { + let v = vector[true, true]; + (v[0u8]); + //^ bool + } + } + """) + + fun `test vector struct index expr`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + fun test_vector() { + let x = X { + value: 2u8 + }; + let v = vector[x, x]; + v[0].value; + //^ u8 + } + } + """) + + fun `test vector borrow mut 0`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has copy, key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (v[0]); + //^ 0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test vector borrow mut 1`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (&mut v[0]); + //^ &mut 0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test vector borrow mut 2`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + ((&mut v[0]).field.value); + //^ bool + } + } + """) + + fun `test vector borrow mut 3`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (&v[0]); + //^ &0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test vector borrow mut 4`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + ((&v[1]).field.value); + //^ bool + } + } + """) } From bffc8709fd8901d822faea408c7089bf168f72b3 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 30 Jun 2024 18:00:36 +0300 Subject: [PATCH 03/10] tests for index expr brace matching --- .../org/move/ide/typing/BraceMatcherTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/kotlin/org/move/ide/typing/BraceMatcherTest.kt b/src/test/kotlin/org/move/ide/typing/BraceMatcherTest.kt index a8d3be891..65702af60 100644 --- a/src/test/kotlin/org/move/ide/typing/BraceMatcherTest.kt +++ b/src/test/kotlin/org/move/ide/typing/BraceMatcherTest.kt @@ -26,8 +26,24 @@ class BraceMatcherTest : MvTypingTestCase() { "script { fun main(/*caret*/) {}}" ) + fun `test pair square brackets for vector lit`() = doTest( + "module 0x1::m { fun main() { vector/*caret*/ }}", + '[', + "module 0x1::m { fun main() { vector[/*caret*/] }}" + ) + + fun `test pair square brackets for index expr`() = doTest( + "module 0x1::m { fun main() { v/*caret*/ }}", + '[', + "module 0x1::m { fun main() { v[/*caret*/] }}" + ) + fun `test match parens`() = doMatch("script { fun main/*caret*/(x: u8) {}}", ")") + fun `test match square brackets vector lit`() = doMatch("module 0x1::m { fun main() { vector/*caret*/[] }}", "]") + + fun `test match square brackets index expr`() = doMatch("module 0x1::m { fun main() { v/*caret*/[] }}", "]") + fun `test match angle brackets`() = doMatch("script { fun main/*caret*/(x: u8) {}}", ">") fun `test match angle with abilities`() = From 8a207a6f2461438a17a8973d8300b4bd2ac1a414 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 30 Jun 2024 19:03:40 +0300 Subject: [PATCH 04/10] add feature flag for index expr --- .../cli/settings/MvProjectSettingsService.kt | 4 + .../settings/MvProjectSettingsServiceBase.kt | 4 +- .../settings/PerProjectAptosConfigurable.kt | 8 + .../core/types/infer/TypeInferenceWalker.kt | 7 + .../org/move/utils/tests/MvLightTestBase.kt | 5 +- .../org/move/utils/tests/MvProjectTestBase.kt | 6 +- .../kotlin/org/move/utils/tests/MvTestBase.kt | 28 +- .../kotlin/org/move/utils/tests/Settings.kt | 5 - .../utils/tests/parser/MoveParsingTestCase.kt | 11 +- .../annotator/HighlightingAnnotatorTest.kt | 105 +++++--- .../inspections/MvTypeCheckInspectionTest.kt | 14 + .../lang/completion/KeywordCompletionTest.kt | 43 +-- .../move/lang/parser/CompleteParsingTest.kt | 10 +- .../ResolveResourceAccessSpecifiersTest.kt | 47 ++-- .../lang/types/ExpressionTypeInferenceTest.kt | 26 +- .../move/lang/types/ExpressionTypesTest.kt | 189 ------------- .../lang/types/compilerV2/IndexExprTypes.kt | 252 ++++++++++++++++++ 17 files changed, 455 insertions(+), 309 deletions(-) create mode 100644 src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt index b11e11aea..adc9baf63 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt @@ -37,6 +37,7 @@ class MvProjectSettingsService( val dumpStateOnTestFailure: Boolean get() = state.dumpStateOnTestFailure val enableResourceAccessControl: Boolean get() = state.enableResourceAccessControl + val enableIndexExpr: Boolean get() = state.enableIndexExpr val addCompilerV2CLIFlags: Boolean get() = state.addCompilerV2CLIFlags // default values for settings @@ -50,6 +51,9 @@ class MvProjectSettingsService( @AffectsParseTree var enableResourceAccessControl: Boolean by property(false) + @AffectsHighlighting + var enableIndexExpr: Boolean by property(false) + @AffectsMoveProjectsMetadata var fetchAptosDeps: Boolean by property(false) diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsServiceBase.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsServiceBase.kt index 422af25d7..316f8d93c 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsServiceBase.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsServiceBase.kt @@ -51,10 +51,10 @@ abstract class MvProjectSettingsServiceBase>( } @TestOnly - fun modifyTemporary(parentDisposable: Disposable, modifySettings: (T) -> Unit) { + fun modifyTemporary(testRootDisposable: Disposable, modifySettings: (T) -> Unit) { val oldState = state loadState(oldState.copy().also(modifySettings)) - Disposer.register(parentDisposable) { + Disposer.register(testRootDisposable) { loadState(oldState) } } diff --git a/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt b/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt index 7e0f097af..bd8aaef2b 100644 --- a/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt +++ b/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt @@ -51,6 +51,14 @@ class PerProjectAptosConfigurable(val project: Project): BoundConfigurable("Apto ) .bindSelected(state::enableResourceAccessControl) } + row { + checkBox("Enable indexing") + .comment( + "Enables resource and vector indexing (i.e. v[0], R[@0x1]) " + + "for the Move files." + ) + .bindSelected(state::enableIndexExpr) + } } group("Command Line Options") { row { diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index 0474b83b8..99bbb25fd 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import org.move.cli.settings.debugErrorOrFallback import org.move.cli.settings.isDebugModeEnabled +import org.move.cli.settings.moveSettings import org.move.ide.formatter.impl.location import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* @@ -658,6 +659,12 @@ class TypeInferenceWalker( private fun inferIndexExprTy(indexExpr: MvIndexExpr): Ty { val receiverTy = indexExpr.receiverExpr.inferType() val argTy = indexExpr.argExpr.inferType() + + // compiler v2 only in non-msl + if (!ctx.msl && !project.moveSettings.enableIndexExpr) { + return TyUnknown + } + return when (receiverTy) { is TyVector -> { // argExpr can be either TyInteger or TyRange diff --git a/src/main/kotlin/org/move/utils/tests/MvLightTestBase.kt b/src/main/kotlin/org/move/utils/tests/MvLightTestBase.kt index c1888e198..1aaaf6c31 100644 --- a/src/main/kotlin/org/move/utils/tests/MvLightTestBase.kt +++ b/src/main/kotlin/org/move/utils/tests/MvLightTestBase.kt @@ -10,9 +10,6 @@ abstract class MvLightTestBase: BasePlatformTestCase() { val isDebugMode = this.findAnnotationInstance()?.enabled ?: true setRegistryKey("org.move.debug.enabled", isDebugMode) - val isResourceAccess = this.findAnnotationInstance() != null - project.moveSettings.modifyTemporary(testRootDisposable) { - it.enableResourceAccessControl = isResourceAccess - } + this.handleCompilerV2Annotations(project) } } \ No newline at end of file diff --git a/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt b/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt index acca660c1..4da013c1f 100644 --- a/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt +++ b/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt @@ -34,11 +34,7 @@ abstract class MvProjectTestBase: CodeInsightFixtureTestCase()?.enabled ?: true setRegistryKey("org.move.debug.enabled", isDebugMode) - val isResourceAccess = this.findAnnotationInstance() != null - // triggers projects refresh - project.moveSettings.modify { - it.enableResourceAccessControl = isResourceAccess - } + this.handleCompilerV2Annotations(project) } override fun tearDown() { diff --git a/src/main/kotlin/org/move/utils/tests/MvTestBase.kt b/src/main/kotlin/org/move/utils/tests/MvTestBase.kt index 753a118bf..39bd1f39f 100644 --- a/src/main/kotlin/org/move/utils/tests/MvTestBase.kt +++ b/src/main/kotlin/org/move/utils/tests/MvTestBase.kt @@ -6,9 +6,16 @@ package org.move.utils.tests import com.intellij.codeInspection.InspectionProfileEntry +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.psi.PsiElement +import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.enableInspectionTool import org.intellij.lang.annotations.Language +import org.move.cli.settings.MvProjectSettingsService +import org.move.cli.settings.moveSettings +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.base.MvTestCase import org.move.utils.tests.base.TestCase import org.move.utils.tests.base.findElementsWithDataAndOffsetInEditor @@ -26,10 +33,29 @@ annotation class DebugMode(val enabled: Boolean) @Retention(AnnotationRetention.RUNTIME) annotation class WithEnabledInspections(vararg val inspections: KClass) +enum class CompilerV2Feat { + INDEXING, RESOURCE_CONTROL; +} + @Inherited @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) -annotation class EnableResourceAccessControl +annotation class CompilerV2Features(vararg val features: CompilerV2Feat) + +fun UsefulTestCase.handleCompilerV2Annotations(project: Project) { + val enabledCompilerV2 = this.findAnnotationInstance() + if (enabledCompilerV2 != null) { + // triggers projects refresh + project.moveSettings.modifyTemporary(this.testRootDisposable) { + for (feature in enabledCompilerV2.features) { + when (feature) { + RESOURCE_CONTROL -> it.enableResourceAccessControl = true + INDEXING -> it.enableIndexExpr = true + } + } + } + } +} abstract class MvTestBase: MvLightTestBase(), MvTestCase { diff --git a/src/main/kotlin/org/move/utils/tests/Settings.kt b/src/main/kotlin/org/move/utils/tests/Settings.kt index fc1f1484b..c5dc5d900 100644 --- a/src/main/kotlin/org/move/utils/tests/Settings.kt +++ b/src/main/kotlin/org/move/utils/tests/Settings.kt @@ -5,8 +5,3 @@ import junit.framework.TestCase /** Tries to find the specified annotation on the current test method and then on the current class */ inline fun TestCase.findAnnotationInstance(): T? = javaClass.getMethod(name).getAnnotation(T::class.java) ?: javaClass.getAnnotation(T::class.java) - -inline fun MvProjectTestBase.findAnnotationInstance(): T? = - javaClass.getMethod(name).getAnnotation(T::class.java) ?: javaClass.getAnnotation(T::class.java) - -//annotation class SettingsPrivateKey(val privateKey: String) diff --git a/src/main/kotlin/org/move/utils/tests/parser/MoveParsingTestCase.kt b/src/main/kotlin/org/move/utils/tests/parser/MoveParsingTestCase.kt index da7530c36..bb617f0e4 100644 --- a/src/main/kotlin/org/move/utils/tests/parser/MoveParsingTestCase.kt +++ b/src/main/kotlin/org/move/utils/tests/parser/MoveParsingTestCase.kt @@ -3,14 +3,12 @@ package org.move.utils.tests.parser import com.intellij.testFramework.ParsingTestCase import org.jetbrains.annotations.NonNls import org.move.cli.settings.MvProjectSettingsService -import org.move.cli.settings.moveSettings import org.move.lang.MoveParserDefinition -import org.move.utils.tests.EnableResourceAccessControl import org.move.utils.tests.base.TestCase -import org.move.utils.tests.findAnnotationInstance +import org.move.utils.tests.handleCompilerV2Annotations -abstract class MvParsingTestCase(@NonNls dataPath: String) : ParsingTestCase( +abstract class MvParsingTestCase(@NonNls dataPath: String): ParsingTestCase( "org/move/lang/parser/$dataPath", "move", true, @@ -21,10 +19,7 @@ abstract class MvParsingTestCase(@NonNls dataPath: String) : ParsingTestCase( project.registerService(MvProjectSettingsService::class.java) - val isResourceAccess = this.findAnnotationInstance() != null - project.moveSettings.modifyTemporary(testRootDisposable) { - it.enableResourceAccessControl = isResourceAccess - } + this.handleCompilerV2Annotations(project) } override fun getTestDataPath(): String = "src/test/resources" diff --git a/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt b/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt index ce90d8ccd..c110353fe 100644 --- a/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt +++ b/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt @@ -1,23 +1,27 @@ package org.move.ide.annotator import org.move.ide.colors.MvColor -import org.move.utils.tests.EnableResourceAccessControl +import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.AnnotatorTestCase -class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class) { +class HighlightingAnnotatorTest: AnnotatorTestCase(HighlightingAnnotator::class) { override fun setUp() { super.setUp() annotationFixture.registerSeverities(MvColor.values().map(MvColor::testSeverity)) } - fun `test block comment do not break the highlighting`() = checkHighlighting(""" + fun `test block comment do not break the highlighting`() = checkHighlighting( + """ /* module M */ module M { fun call() {} } - """) + """ + ) - fun `test function calls annotated`() = checkHighlighting(""" + fun `test function calls annotated`() = checkHighlighting( + """ module 0x1::string { public fun mycall() {} } @@ -30,9 +34,11 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class mycall_alias() } } - """) + """ + ) - fun `test variable names annotated`() = checkHighlighting(""" + fun `test variable names annotated`() = checkHighlighting( + """ module M { const MAX_INT: u8 = 255; @@ -42,7 +48,8 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class MAX_INT; } } - """) + """ + ) fun `test types highlighed`() = checkHighlighting( """ @@ -88,7 +95,8 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class """ ) - fun `test highlight view functions`() = checkHighlighting(""" + fun `test highlight view functions`() = checkHighlighting( + """ module 0x1::m { #[view] public fun get_pool() {} @@ -96,25 +104,30 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class get_pool(); } } - """) + """ + ) - fun `test highlight entry functions`() = checkHighlighting(""" + fun `test highlight entry functions`() = checkHighlighting( + """ module 0x1::m { public entry fun get_pool() {} fun main() { get_pool(); } } - """) + """ + ) - fun `test highlight inline functions`() = checkHighlighting(""" + fun `test highlight inline functions`() = checkHighlighting( + """ module 0x1::m { public inline fun get_pool() {} fun main() { get_pool(); } } - """) + """ + ) fun `test function param named as builtin type`() = checkHighlighting( """ @@ -138,7 +151,7 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class fun `test generic type parameters highlighted`() = checkHighlighting( """ - module M { + module 0x1::M { struct MyStruct<T> { field: T } @@ -179,16 +192,19 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class """ ) - fun `test spec keywords are highlighted`() = checkHighlighting(""" + fun `test spec keywords are highlighted`() = checkHighlighting( + """ module M { spec module { assume 1 == 1; assert 1 == 1; } } - """) + """ + ) - fun `test address highlighting`() = checkHighlighting(""" + fun `test address highlighting`() = checkHighlighting( + """ module 0x1::M { #[test(acc =
@0x42
, acc2 =
@0x43
)] fun m() { @@ -196,26 +212,32 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class borrow_global_mut
(
@0x1
) } } - """) + """ + ) - fun `test macros are highlighted`() = checkHighlighting(""" + fun `test macros are highlighted`() = checkHighlighting( + """ module 0x1::M { fun m() { assert!(true, 1); } } - """) + """ + ) - fun `test integer highlighting`() = checkHighlighting(""" + fun `test integer highlighting`() = checkHighlighting( + """ module 0x1::main { fun main() { let a = 0x123456; } } - """) + """ + ) - fun `test structs and struct fields are highlighted`() = checkHighlighting(""" + fun `test structs and struct fields are highlighted`() = checkHighlighting( + """ module 0x1::m { struct MyS { field1: u8, @@ -228,9 +250,11 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class MyS { field1: 1, field2: 2 } } } - """) + """ + ) - fun `test highlight objects`() = checkHighlighting(""" + fun `test highlight objects`() = checkHighlighting( + """ module 0x1::m { struct Res {} struct ResKey has key {} @@ -247,9 +271,11 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class res_store; } } - """) + """ + ) - fun `test highlight references`() = checkHighlighting(""" + fun `test highlight references`() = checkHighlighting( + """ module 0x1::m { struct Res {} struct ResKey has key {} @@ -278,18 +304,22 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class ref_res_store_no_drop; } } - """) + """ + ) - fun `test for loop keywords`() = checkHighlighting(""" + fun `test for loop keywords`() = checkHighlighting( + """ module 0x1::m { fun main() { let for = 1; for (i in 1..10) {}; } } - """) + """ + ) - fun `test highlight methods`() = checkHighlighting(""" + fun `test highlight methods`() = checkHighlighting( + """ module 0x1::m { struct S { field: u8 } fun receiver(self: S, self: u8): u8 { @@ -299,13 +329,16 @@ class HighlightingAnnotatorTest : AnnotatorTestCase(HighlightingAnnotator::class s.receiver(); } } - """) + """ + ) - @EnableResourceAccessControl - fun `test resource access control keywords highlighting`() = checkHighlighting(""" + @CompilerV2Features(RESOURCE_CONTROL) + fun `test resource access control keywords highlighting`() = checkHighlighting( + """ module 0x1::m { fun f_multiple() reads R writes T, S reads G {} fun f_multiple2() pure {} } - """) + """ + ) } diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index 612d94c69..bad97bc9f 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -1,5 +1,7 @@ package org.move.ide.inspections +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class MvTypeCheckInspectionTest: InspectionTestBase(MvTypeCheckInspection::class) { @@ -1536,6 +1538,17 @@ module 0x1::pool { """ ) + @CompilerV2Features() + fun `test no error receiver of vector index expr without compiler v2 feature`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[0]; + } + } + """) + + @CompilerV2Features(INDEXING) fun `test error receiver of vector index expr`() = checkByText(""" module 0x1::m { fun main() { @@ -1545,6 +1558,7 @@ module 0x1::pool { } """) + @CompilerV2Features(INDEXING) fun `test error receiver of resource index expr`() = checkByText(""" module 0x1::m { fun main() { diff --git a/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt b/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt index 2525728dd..f7d2302fc 100644 --- a/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt +++ b/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt @@ -1,9 +1,10 @@ package org.move.lang.completion -import org.move.utils.tests.EnableResourceAccessControl +import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.completion.CompletionTestCase -class KeywordCompletionTest : CompletionTestCase() { +class KeywordCompletionTest: CompletionTestCase() { fun `test address`() = doSingleCompletion( """ addr/*caret*/ @@ -139,7 +140,8 @@ class KeywordCompletionTest : CompletionTestCase() { """ ) - fun `test spec fun`() = doSingleCompletion(""" + fun `test spec fun`() = doSingleCompletion( + """ module 0x1::M { spec f/*caret*/ } @@ -147,9 +149,11 @@ class KeywordCompletionTest : CompletionTestCase() { module 0x1::M { spec fun /*caret*/ } - """) + """ + ) - fun `test spec module`() = doSingleCompletion(""" + fun `test spec module`() = doSingleCompletion( + """ module 0x1::M { spec mod/*caret*/ } @@ -157,9 +161,11 @@ class KeywordCompletionTest : CompletionTestCase() { module 0x1::M { spec module /*caret*/ } - """) + """ + ) - fun `test spec schema`() = doSingleCompletion(""" + fun `test spec schema`() = doSingleCompletion( + """ module 0x1::M { spec sch/*caret*/ } @@ -167,7 +173,8 @@ class KeywordCompletionTest : CompletionTestCase() { module 0x1::M { spec schema /*caret*/ } - """) + """ + ) fun `test public`() = completionFixture.checkContainsCompletion( """ @@ -546,7 +553,8 @@ class KeywordCompletionTest : CompletionTestCase() { """ ) - fun `test spec module space exists`() = doSingleCompletion(""" + fun `test spec module space exists`() = doSingleCompletion( + """ module 0x1::M { spec mo/*caret*/ {} } @@ -554,9 +562,11 @@ class KeywordCompletionTest : CompletionTestCase() { module 0x1::M { spec module/*caret*/ {} } - """) + """ + ) - fun `test else after if block`() = doSingleCompletion(""" + fun `test else after if block`() = doSingleCompletion( + """ module 0x1::M { fun call() { if (true) { @@ -572,9 +582,11 @@ class KeywordCompletionTest : CompletionTestCase() { } else /*caret*/ } } - """) + """ + ) - fun `test continue inside while`() = doSingleCompletion(""" + fun `test continue inside while`() = doSingleCompletion( + """ module 0x1::Main { fun call() { while (true) { @@ -590,7 +602,8 @@ class KeywordCompletionTest : CompletionTestCase() { } } } - """) + """ + ) // fun `test bool completion in field initializer`() = doSingleCompletion(""" //module 0x1::main { @@ -617,7 +630,7 @@ class KeywordCompletionTest : CompletionTestCase() { """ ) - @EnableResourceAccessControl + @CompilerV2Features(RESOURCE_CONTROL) fun `test completion for resource access modifiers`() = checkContainsCompletion( listOf("reads", "writes", "pure", "acquires"), """ diff --git a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt index 5af0fc569..03e0d2e0d 100644 --- a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt +++ b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt @@ -1,9 +1,10 @@ package org.move.lang.parser -import org.move.utils.tests.EnableResourceAccessControl +import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.parser.MvParsingTestCase -class CompleteParsingTest : MvParsingTestCase("complete") { +class CompleteParsingTest: MvParsingTestCase("complete") { fun `test comments`() = doTest() fun `test addresses`() = doTest() fun `test attributes`() = doTest() @@ -43,9 +44,10 @@ class CompleteParsingTest : MvParsingTestCase("complete") { fun `test macros`() = doTest() fun `test loops`() = doTest() - @EnableResourceAccessControl - fun `test access control`() = doTest() + // compiler v2 fun `test index expr`() = doTest() + @CompilerV2Features(RESOURCE_CONTROL) + fun `test access control`() = doTest() fun doTest() { super.doTest(true, true) diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt index 3af3bfd33..49c4a9b55 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt @@ -1,38 +1,46 @@ package org.move.lang.resolve -import org.move.utils.tests.EnableResourceAccessControl +import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.resolve.ResolveTestCase -@EnableResourceAccessControl +@CompilerV2Features(RESOURCE_CONTROL) class ResolveResourceAccessSpecifiersTest: ResolveTestCase() { - fun `test resolve type for reads`() = checkByCode(""" + fun `test resolve type for reads`() = checkByCode( + """ module 0x1::main { struct S { field: u8 } //X fun main() reads S {} //^ } - """) + """ + ) - fun `test resolve type for write`() = checkByCode(""" + fun `test resolve type for write`() = checkByCode( + """ module 0x1::main { struct S { field: u8 } //X fun main() writes S {} //^ } - """) + """ + ) - fun `test resolve type for acquires`() = checkByCode(""" + fun `test resolve type for acquires`() = checkByCode( + """ module 0x1::main { struct S { field: u8 } //X fun main() acquires S {} //^ } - """) + """ + ) - fun `test resolve address function in address specifier`() = checkByCode(""" + fun `test resolve address function in address specifier`() = checkByCode( + """ module 0x1::main { struct S { field: u8 } fun main(value: u8) reads *(make_up_address(value)) {} @@ -40,18 +48,22 @@ class ResolveResourceAccessSpecifiersTest: ResolveTestCase() { fun make_up_address(x: u8): address { @0x1 } //X } - """) + """ + ) - fun `test resolve module with wildcard`() = checkByCode(""" + fun `test resolve module with wildcard`() = checkByCode( + """ module 0x1::main { //X struct S { field: u8 } fun main(value: u8) reads 0x1::main::* {} //^ } - """) + """ + ) - fun `test resolve parameter for address function`() = checkByCode(""" + fun `test resolve parameter for address function`() = checkByCode( + """ module 0x1::main { struct S { field: u8 } fun main( @@ -61,9 +73,11 @@ class ResolveResourceAccessSpecifiersTest: ResolveTestCase() { //^ fun make_up_address(x: u8): address { @0x1 } } - """) + """ + ) - fun `test address function can be from another module`() = checkByCode(""" + fun `test address function can be from another module`() = checkByCode( + """ module 0x1::signer { public native fun address_of(s: &signer); //X @@ -74,5 +88,6 @@ class ResolveResourceAccessSpecifiersTest: ResolveTestCase() { fun main(s: &signer) reads *(signer::address_of(s)) {} //^ } - """) + """ + ) } \ No newline at end of file diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt index 099fffe60..7d2e94a7c 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt @@ -1,5 +1,7 @@ package org.move.lang.types +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.types.TypificationTestCase class ExpressionTypeInferenceTest: TypificationTestCase() { @@ -561,28 +563,4 @@ module 0x1::main { } } """) - - fun `test integer type inference with vector index expr 1`() = testExpr(""" - module 0x1::m { - fun main() { - let a = 1; - let v = vector[2u8]; - a == v[0]; - a; - //^ u8 - } - } - """) - - fun `test integer type inference with vector index expr 2`() = testExpr(""" - module 0x1::m { - fun main() { - let a = 1u8; - let v = vector[2]; - a == v[0]; - v; - //^ vector - } - } - """) } diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt index ac2b463c9..019add433 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt @@ -1195,19 +1195,6 @@ module 0x1::main { """ ) - fun `test spec index expr`() = testExpr( - """ - module 0x1::m { - spec module { - let v = vector[false]; - let a = v[0]; - a; - //^ bool - } - } - """ - ) - fun `test range expr`() = testExpr( """ module 0x1::m { @@ -1834,180 +1821,4 @@ module 0x1::main { } } """) - - fun `test vector index expr`() = testExpr(""" - module 0x1::m { - fun test_vector() { - let v = vector[true, true]; - (v[0]); - //^ bool - } - } - """) - - fun `test vector index expr u8`() = testExpr(""" - module 0x1::m { - fun test_vector() { - let v = vector[true, true]; - (v[0u8]); - //^ bool - } - } - """) - - fun `test vector struct index expr`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - fun test_vector() { - let x = X { - value: 2u8 - }; - let v = vector[x, x]; - v[0].value; - //^ u8 - } - } - """) - - fun `test vector borrow mut 0`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - struct Y has copy, key, drop { - field: T - } - fun test_vector_borrow_mut() { - let x1 = X { - value: true - }; - let x2 = X { - value: false - }; - let y1 = Y { - field: x1 - }; - let y2 = Y { - field: x2 - }; - let v = vector[y1, y2]; - (v[0]); - //^ 0x1::m::Y<0x1::m::X> - } - } - """) - - fun `test vector borrow mut 1`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - struct Y has key, drop { - field: T - } - fun test_vector_borrow_mut() { - let x1 = X { - value: true - }; - let x2 = X { - value: false - }; - let y1 = Y { - field: x1 - }; - let y2 = Y { - field: x2 - }; - let v = vector[y1, y2]; - (&mut v[0]); - //^ &mut 0x1::m::Y<0x1::m::X> - } - } - """) - - fun `test vector borrow mut 2`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - struct Y has key, drop { - field: T - } - fun test_vector_borrow_mut() { - let x1 = X { - value: true - }; - let x2 = X { - value: false - }; - let y1 = Y { - field: x1 - }; - let y2 = Y { - field: x2 - }; - let v = vector[y1, y2]; - ((&mut v[0]).field.value); - //^ bool - } - } - """) - - fun `test vector borrow mut 3`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - struct Y has key, drop { - field: T - } - fun test_vector_borrow_mut() { - let x1 = X { - value: true - }; - let x2 = X { - value: false - }; - let y1 = Y { - field: x1 - }; - let y2 = Y { - field: x2 - }; - let v = vector[y1, y2]; - (&v[0]); - //^ &0x1::m::Y<0x1::m::X> - } - } - """) - - fun `test vector borrow mut 4`() = testExpr(""" - module 0x1::m { - struct X has copy, drop, store { - value: M - } - struct Y has key, drop { - field: T - } - fun test_vector_borrow_mut() { - let x1 = X { - value: true - }; - let x2 = X { - value: false - }; - let y1 = Y { - field: x1 - }; - let y2 = Y { - field: x2 - }; - let v = vector[y1, y2]; - ((&v[1]).field.value); - //^ bool - } - } - """) } diff --git a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt new file mode 100644 index 000000000..3791e6bee --- /dev/null +++ b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt @@ -0,0 +1,252 @@ +package org.move.lang.types.compilerV2 + +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features +import org.move.utils.tests.types.TypificationTestCase + +@CompilerV2Features(INDEXING) +class IndexExprTypes: TypificationTestCase() { + @CompilerV2Features() + fun `test unknown without enabled feature`() = testExpr( + """ + module 0x1::m { + fun test_vector() { + let v = vector[true, true]; + (v[0]); + //^ + } + } + """ + ) + + @CompilerV2Features() + fun `test spec index expr`() = testExpr( + """ + module 0x1::m { + spec module { + let v = vector[false]; + let a = v[0]; + a; + //^ bool + } + } + """ + ) + + fun `test vector index expr`() = testExpr( + """ + module 0x1::m { + fun test_vector() { + let v = vector[true, true]; + (v[0]); + //^ bool + } + } + """ + ) + + fun `test vector index expr u8`() = testExpr( + """ + module 0x1::m { + fun test_vector() { + let v = vector[true, true]; + (v[0u8]); + //^ bool + } + } + """ + ) + + fun `test vector struct index expr`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + fun test_vector() { + let x = X { + value: 2u8 + }; + let v = vector[x, x]; + v[0].value; + //^ u8 + } + } + """ + ) + + fun `test vector borrow mut 0`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has copy, key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (v[0]); + //^ 0x1::m::Y<0x1::m::X> + } + } + """ + ) + + fun `test vector borrow mut 1`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (&mut v[0]); + //^ &mut 0x1::m::Y<0x1::m::X> + } + } + """ + ) + + fun `test vector borrow mut 2`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + ((&mut v[0]).field.value); + //^ bool + } + } + """ + ) + + fun `test vector borrow mut 3`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + (&v[0]); + //^ &0x1::m::Y<0x1::m::X> + } + } + """ + ) + + fun `test vector borrow mut 4`() = testExpr( + """ + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + fun test_vector_borrow_mut() { + let x1 = X { + value: true + }; + let x2 = X { + value: false + }; + let y1 = Y { + field: x1 + }; + let y2 = Y { + field: x2 + }; + let v = vector[y1, y2]; + ((&v[1]).field.value); + //^ bool + } + } + """ + ) + + + fun `test integer type inference with vector index expr 1`() = testExpr(""" + module 0x1::m { + fun main() { + let a = 1; + let v = vector[2u8]; + a == v[0]; + a; + //^ u8 + } + } + """) + + fun `test integer type inference with vector index expr 2`() = testExpr(""" + module 0x1::m { + fun main() { + let a = 1u8; + let v = vector[2]; + a == v[0]; + v; + //^ vector + } + } + """) +} \ No newline at end of file From e9b0f0108ad27c7e871b7eaf46f01c7d18392dc5 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sun, 30 Jun 2024 21:57:29 +0300 Subject: [PATCH 05/10] resource index expr support --- .../MvMissingAcquiresInspection.kt | 36 ++++-- .../move/lang/core/types/infer/Acquires.kt | 12 ++ .../core/types/infer/TypeInferenceWalker.kt | 20 ++- .../MvMissingAcquiresInspectionTest.kt | 44 +++++++ .../inspections/MvTypeCheckInspectionTest.kt | 31 +++++ .../MvUnusedAcquiresTypeInspectionTest.kt | 46 +++++++ .../org/move/lang/resolve/ResolveTypesTest.kt | 14 +++ .../lang/types/compilerV2/IndexExprTypes.kt | 115 ++++++++++++++++++ 8 files changed, 303 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt index edc6a506b..029db9472 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt @@ -2,6 +2,7 @@ package org.move.ide.inspections import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder +import org.move.cli.settings.moveSettings import org.move.ide.inspections.fixes.AddAcquiresFix import org.move.ide.presentation.fullnameNoArgs import org.move.lang.core.psi.* @@ -9,35 +10,46 @@ import org.move.lang.core.psi.ext.MvCallable import org.move.lang.core.psi.ext.isInline import org.move.lang.core.types.infer.acquiresContext import org.move.lang.core.types.infer.inference +import org.move.lang.core.types.ty.Ty import org.move.lang.core.types.ty.TyStruct import org.move.lang.core.types.ty.TyTypeParameter import org.move.lang.moveProject -class MvMissingAcquiresInspection : MvLocalInspectionTool() { +class MvMissingAcquiresInspection: MvLocalInspectionTool() { override val isSyntaxOnly: Boolean get() = true override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = - object : MvVisitor() { - override fun visitCallExpr(o: MvCallExpr) = visitCallable(o) - override fun visitMethodCall(o: MvMethodCall) = visitCallable(o) + object: MvVisitor() { + override fun visitIndexExpr(o: MvIndexExpr) { + if (o.project.moveSettings.enableIndexExpr) { + visitStorageAccessElement(o) + } + } - private fun visitCallable(callable: MvCallable) { - val outerFunction = callable.containingFunction ?: return + override fun visitCallExpr(o: MvCallExpr) = visitStorageAccessElement(o) + override fun visitMethodCall(o: MvMethodCall) = visitStorageAccessElement(o) + + private fun visitStorageAccessElement(element: MvElement) { + val outerFunction = element.containingFunction ?: return if (outerFunction.isInline) return - val acquiresContext = callable.moveProject?.acquiresContext ?: return + val acquiresContext = element.moveProject?.acquiresContext ?: return val inference = outerFunction.inference(false) val existingTypes = acquiresContext.getFunctionTypes(outerFunction) - val existingTypeNames = - existingTypes.map { it.fullnameNoArgs() }.toSet() - val callExprTypes = acquiresContext.getCallTypes(callable, inference) + val callAcquiresTypes = when (element) { + is MvCallable -> acquiresContext.getCallTypes(element, inference) + is MvIndexExpr -> acquiresContext.getIndexExprTypes(element, inference) + else -> return + } + val existingTypeNames = + existingTypes.map { it.fullnameNoArgs() }.toSet() val currentModule = outerFunction.module ?: return val missingTypes = - callExprTypes.mapNotNull { acqTy -> + callAcquiresTypes.mapNotNull { acqTy -> when (acqTy) { is TyTypeParameter -> acqTy.origin.takeIf { tyOrigin -> existingTypes.all { tyOrigin != it } } @@ -60,7 +72,7 @@ class MvMissingAcquiresInspection : MvLocalInspectionTool() { val name = outerFunction.name ?: return val missingNames = missingTypes.mapNotNull { it.name } holder.registerProblem( - callable, + element, "Function '$name' is not marked as 'acquires ${missingNames.joinToString()}'", ProblemHighlightType.GENERIC_ERROR, AddAcquiresFix(outerFunction, missingNames) diff --git a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt index 8365e5640..a5c599cd4 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt @@ -9,11 +9,14 @@ import com.jetbrains.rd.util.concurrentMapOf import org.move.cli.MoveProject import org.move.lang.core.psi.MvCallExpr import org.move.lang.core.psi.MvFunction +import org.move.lang.core.psi.MvIndexExpr import org.move.lang.core.psi.acquiresPathTypes import org.move.lang.core.psi.ext.MvCallable import org.move.lang.core.psi.ext.isInline +import org.move.lang.core.psi.ext.receiverExpr import org.move.lang.core.types.ty.Ty import org.move.lang.core.types.ty.TyFunction +import org.move.lang.core.types.ty.TyStruct import org.move.lang.moveProject val ACQUIRES_TYPE_CONTEXT: Key> = Key.create("ACQUIRES_TYPE_CONTEXT") @@ -66,6 +69,15 @@ class AcquiresTypeContext { } } } + + fun getIndexExprTypes(indexExpr: MvIndexExpr, inference: InferenceResult): List { + val receiverTy = inference.getExprType(indexExpr.receiverExpr) + return if (receiverTy is TyStruct) { + listOf(receiverTy) + } else { + emptyList() + } + } } fun MvFunction.getInnerAcquiresTypes(): List { diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index 99bbb25fd..d8a64df5c 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -10,6 +10,7 @@ import org.move.ide.formatter.impl.location import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* import org.move.lang.core.types.ty.* +import org.move.lang.core.types.ty.TyInfer.TyVar import org.move.lang.core.types.ty.TyReference.Companion.autoborrow import org.move.stdext.RsResult import org.move.stdext.chain @@ -296,8 +297,14 @@ class TypeInferenceWalker( is MvConst -> item.type?.loweredType(msl) ?: TyUnknown is MvGlobalVariableStmt -> item.type?.loweredType(true) ?: TyUnknown is MvStructField -> item.type?.loweredType(msl) ?: TyUnknown - // only occurs in the invalid code statements - is MvStruct -> TyUnknown + is MvStruct -> { + if (project.moveSettings.enableIndexExpr && refExpr.parent is MvIndexExpr) { + TyLowering.lowerPath(refExpr.path, item, ctx.msl) + } else { + // invalid statements + TyUnknown + } + } else -> debugErrorOrFallback( "Referenced item ${item.elementType} " + "of ref expr `${refExpr.text}` at ${refExpr.location} cannot be inferred into type", @@ -671,9 +678,16 @@ class TypeInferenceWalker( when (argTy) { is TyRange -> receiverTy is TyInteger, is TyInfer.IntVar, is TyNum -> receiverTy.item - else -> TyUnknown + else -> { + coerce(indexExpr.argExpr, argTy, if (ctx.msl) TyNum else TyInteger.DEFAULT) + TyUnknown + } } } + is TyStruct -> { + coerce(indexExpr.argExpr, argTy, TyAddress) + receiverTy + } else -> { ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) TyUnknown diff --git a/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt index 2fbb000ed..7d503351d 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt @@ -1,5 +1,7 @@ package org.move.ide.inspections +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class MvMissingAcquiresInspectionTest : InspectionTestBase(MvMissingAcquiresInspection::class) { @@ -207,4 +209,46 @@ module 0x1::main { } } """) + + fun `test no error with index if unsupported`() = checkErrors( + """ + module 0x1::M { + struct Loan has key {} + fun main() { + Loan[@0x1]; + } + } + """ + ) + + @CompilerV2Features(INDEXING) + fun `test missing acquires with index expr`() = checkErrors( + """ + module 0x1::M { + struct Loan has key {} + fun main() { + Loan[@0x1]; + } + } + """ + ) + + fun `test no missing acquires for item from another module`() = checkErrors( + """ + module 0x1::s { + struct S has key { + } + public fun call() acquires S { + borrow_global(@0x1); + } + + } + module 0x1::M { + use 0x1::s::call; + fun main() { + call(); + } + } + """ + ) } diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index bad97bc9f..9e379d7c9 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -1567,4 +1567,35 @@ module 0x1::pool { } } """) + + @CompilerV2Features(INDEXING) + fun `test vector index expr argument is not an integer`() = checkByText(""" + module 0x1::m { + fun main() { + let v = vector[1, 2]; + v[false]; + } + } + """) + + @CompilerV2Features(INDEXING) + fun `test vector index expr argument is not an integer in spec`() = checkByText(""" + module 0x1::m { + spec fun main(): u8 { + let v = vector[1, 2]; + v[false]; + 1 + } + } + """) + + @CompilerV2Features(INDEXING) + fun `test resource index expr argument is not an address`() = checkByText(""" + module 0x1::m { + struct S has key {} + fun main() { + S[false]; + } + } + """) } diff --git a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt index adaa0eb33..2db91a57c 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt @@ -1,5 +1,7 @@ package org.move.ide.inspections +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class MvUnusedAcquiresTypeInspectionTest: InspectionTestBase(MvUnusedAcquiresTypeInspection::class) { @@ -144,6 +146,50 @@ module 0x1::main { } inline fun f() { } +} + """ + ) + + fun `test unused acquires with index expr on compiler v1`() = checkWarnings( + """ +module 0x1::main { + struct StakePool has key { + locked_until_secs: u64, + } + fun get_lockup_secs(pool_address: address) acquires StakePool { + StakePool[pool_address]; + } +} + """ + ) + + @CompilerV2Features(INDEXING) + fun `test no unused acquires with index expr`() = checkWarnings( + """ +module 0x1::main { + struct StakePool has key { + locked_until_secs: u64, + } + fun get_lockup_secs(pool_address: address) acquires StakePool { + StakePool[pool_address]; + } +} + """ + ) + + @CompilerV2Features(INDEXING) + fun `test no unused acquires with index expr inside inline function`() = checkWarnings( + """ +module 0x1::main { + struct StakePool has key { + locked_until_secs: u64, + } + fun get_lockup_secs(pool_address: address) acquires StakePool { + f(); + } + inline fun f() { + StakePool[pool_address]; + } } """ ) diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt index 3cd533371..6fadbe2a1 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt @@ -1,5 +1,7 @@ package org.move.lang.resolve +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.resolve.ResolveTestCase class ResolveTypesTest : ResolveTestCase() { @@ -337,4 +339,16 @@ module 0x1::m { } } """) + + @CompilerV2Features(INDEXING) + fun `test resource index expr`() = checkByCode(""" + module 0x1::m { + struct S has key {} + //X + fun main() { + S[@0x1]; + //^ + } + } + """) } diff --git a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt index 3791e6bee..0170dd7e3 100644 --- a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt +++ b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt @@ -249,4 +249,119 @@ class IndexExprTypes: TypificationTestCase() { } } """) + + fun `test resource index expr 0`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_3() acquires R { + (Y>[@0x1]); + //^ 0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test resource index expr ref expr`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_3() acquires R { + Y>[@0x1]; + //^ 0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test resource index expr 1`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_3() acquires R { + ((Y>[@0x1]).field.value); + //^ bool + } + } + """) + + fun `test resource index expr inference`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_3() acquires R { + let a = 1; + a == Y>[@0x1].field.value; + a; + //^ u8 + } + } + """) + + fun `test resource index expr 2`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_4() acquires R { + let addr = @0x1; + let y = &mut Y>[addr]; + y; + //^ &mut 0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test resource index expr 3`() = testExpr(""" + module 0x1::m { + struct X has copy, drop, store { + value: M + } + struct Y has key, drop { + field: T + } + + fun test_resource_4() acquires R { + let addr = @0x1; + let y = &Y>[addr]; + y; + //^ &0x1::m::Y<0x1::m::X> + } + } + """) + + fun `test vector index expr do not disable inference`() = testExpr(""" + module 0x1::m { + fun main() { + let v = vector[1, 2]; + let a = 1; + v[a]; + a + 2u8; + a; + //^ u8 + } + } + """) } \ No newline at end of file From 1d0a56ffb501cf346ddf0134edbb68c4590fa7ab Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Mon, 1 Jul 2024 21:08:32 +0300 Subject: [PATCH 06/10] index expr: missing acquires --- src/main/grammars/MoveParser.bnf | 5 +++ .../MvMissingAcquiresInspection.kt | 13 +++--- .../MvUnusedAcquiresTypeInspection.kt | 45 ++++++++++--------- .../move/lang/core/psi/MvAcquireTypesOwner.kt | 4 ++ .../move/lang/core/types/infer/Acquires.kt | 36 ++++++++++++--- 5 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 src/main/kotlin/org/move/lang/core/psi/MvAcquireTypesOwner.kt diff --git a/src/main/grammars/MoveParser.bnf b/src/main/grammars/MoveParser.bnf index 2fc89601a..ea9f393cb 100644 --- a/src/main/grammars/MoveParser.bnf +++ b/src/main/grammars/MoveParser.bnf @@ -968,6 +968,7 @@ CallExpr ::= (Path &'(') ValueArgumentList { implements = [ "org.move.lang.core.psi.PathExpr" "org.move.lang.core.psi.ext.MvCallable" + "org.move.lang.core.psi.MvAcquireTypesOwner" ] } ValueArgumentList ::= '(' ValueArgumentList_items? ')' { @@ -1030,11 +1031,15 @@ MethodCall ::= IDENTIFIER ColonTypeArgumentList? ValueArgumentList "org.move.lang.core.psi.ext.MvMethodOrField" "org.move.lang.core.psi.ext.MvMethodOrPath" "org.move.lang.core.psi.ext.MvCallable" + "org.move.lang.core.psi.MvAcquireTypesOwner" ] mixin = "org.move.lang.core.psi.ext.MvMethodCallMixin" } IndexExpr ::= Expr IndexArg +{ + implements = [ "org.move.lang.core.psi.MvAcquireTypesOwner" ] +} // Do not inline this rule, it breaks expression parsing private IndexArg ::= '[' Expr ']' diff --git a/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt index 029db9472..45506c6f4 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvMissingAcquiresInspection.kt @@ -10,7 +10,6 @@ import org.move.lang.core.psi.ext.MvCallable import org.move.lang.core.psi.ext.isInline import org.move.lang.core.types.infer.acquiresContext import org.move.lang.core.types.infer.inference -import org.move.lang.core.types.ty.Ty import org.move.lang.core.types.ty.TyStruct import org.move.lang.core.types.ty.TyTypeParameter import org.move.lang.moveProject @@ -21,16 +20,14 @@ class MvMissingAcquiresInspection: MvLocalInspectionTool() { override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object: MvVisitor() { + override fun visitCallExpr(o: MvCallExpr) = visitAcquiredTypesOwner(o) + override fun visitMethodCall(o: MvMethodCall) = visitAcquiredTypesOwner(o) override fun visitIndexExpr(o: MvIndexExpr) { - if (o.project.moveSettings.enableIndexExpr) { - visitStorageAccessElement(o) - } + if (!o.project.moveSettings.enableIndexExpr) return + visitAcquiredTypesOwner(o) } - override fun visitCallExpr(o: MvCallExpr) = visitStorageAccessElement(o) - override fun visitMethodCall(o: MvMethodCall) = visitStorageAccessElement(o) - - private fun visitStorageAccessElement(element: MvElement) { + private fun visitAcquiredTypesOwner(element: MvAcquireTypesOwner) { val outerFunction = element.containingFunction ?: return if (outerFunction.isInline) return diff --git a/src/main/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspection.kt index 9c5829025..75479982a 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspection.kt @@ -7,16 +7,17 @@ import org.move.ide.inspections.fixes.RemoveAcquiresFix import org.move.ide.presentation.fullnameNoArgs import org.move.ide.presentation.itemDeclaredInModule import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.MvCallable +import org.move.lang.core.types.infer.AcquireTypesOwnerVisitor import org.move.lang.core.types.infer.acquiresContext import org.move.lang.core.types.infer.inference import org.move.lang.core.types.infer.loweredType import org.move.lang.moveProject -class MvUnusedAcquiresTypeInspection : MvLocalInspectionTool() { - override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor { - val annotationHolder = Holder(holder) - return object : MvVisitor() { +class MvUnusedAcquiresTypeInspection: MvLocalInspectionTool() { + override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor = + object: MvVisitor() { override fun visitAcquiresType(o: MvAcquiresType) { val function = o.parent as? MvFunction ?: return val currentModule = function.module ?: return @@ -24,11 +25,18 @@ class MvUnusedAcquiresTypeInspection : MvLocalInspectionTool() { val inference = function.inference(false) val callAcquiresTypes = mutableSetOf() - for (callExpr in inference.callableTypes.keys) { - val types = acquiresContext.getCallTypes(callExpr, inference) - callAcquiresTypes.addAll( - types.map { it.fullnameNoArgs() }) + val visitor = object: AcquireTypesOwnerVisitor() { + override fun visitAcquireTypesOwner(acqTypesOwner: MvAcquireTypesOwner) { + val types = + when (acqTypesOwner) { + is MvCallable -> acquiresContext.getCallTypes(acqTypesOwner, inference) + is MvIndexExpr -> acquiresContext.getIndexExprTypes(acqTypesOwner, inference) + else -> error("when is exhaustive") + } + callAcquiresTypes.addAll(types.map { it.fullnameNoArgs() }) + } } + visitor.visitElement(function) val unusedTypeIndices = mutableListOf() val visitedTypes = mutableSetOf() @@ -54,24 +62,21 @@ class MvUnusedAcquiresTypeInspection : MvLocalInspectionTool() { } if (unusedTypeIndices.size == function.acquiresPathTypes.size) { // register whole acquiresType - annotationHolder.registerUnusedAcquires(o) + holder.registerUnusedAcquires(o) return } for (idx in unusedTypeIndices) { - annotationHolder.registerUnusedAcquires(function.acquiresPathTypes[idx]) + holder.registerUnusedAcquires(function.acquiresPathTypes[idx]) } } } - } - class Holder(val problemsHolder: ProblemsHolder) { - fun registerUnusedAcquires(ref: PsiElement) { - problemsHolder.registerProblem( - ref, - "Unused acquires clause", - ProblemHighlightType.LIKE_UNUSED_SYMBOL, - RemoveAcquiresFix(ref) - ) - } + private fun ProblemsHolder.registerUnusedAcquires(ref: PsiElement) { + this.registerProblem( + ref, + "Unused acquires clause", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + RemoveAcquiresFix(ref) + ) } } diff --git a/src/main/kotlin/org/move/lang/core/psi/MvAcquireTypesOwner.kt b/src/main/kotlin/org/move/lang/core/psi/MvAcquireTypesOwner.kt new file mode 100644 index 000000000..39fc69081 --- /dev/null +++ b/src/main/kotlin/org/move/lang/core/psi/MvAcquireTypesOwner.kt @@ -0,0 +1,4 @@ +package org.move.lang.core.psi + +interface MvAcquireTypesOwner: MvElement { +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt index a5c599cd4..5525d2831 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt @@ -1,16 +1,15 @@ package org.move.lang.core.types.infer import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiRecursiveElementVisitor import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import com.jetbrains.rd.util.concurrentMapOf import org.move.cli.MoveProject -import org.move.lang.core.psi.MvCallExpr -import org.move.lang.core.psi.MvFunction -import org.move.lang.core.psi.MvIndexExpr -import org.move.lang.core.psi.acquiresPathTypes +import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.MvCallable import org.move.lang.core.psi.ext.isInline import org.move.lang.core.psi.ext.receiverExpr @@ -33,6 +32,19 @@ val MoveProject.acquiresContext: AcquiresTypeContext }, false) } + +abstract class AcquireTypesOwnerVisitor: PsiRecursiveElementVisitor() { + + abstract fun visitAcquireTypesOwner(acqTypesOwner: MvAcquireTypesOwner) + + override fun visitElement(element: PsiElement) { + when (element) { + is MvAcquireTypesOwner -> visitAcquireTypesOwner(element) + else -> super.visitElement(element) + } + } +} + class AcquiresTypeContext { private val functionTypes: MutableMap> = concurrentMapOf() private val callableTypes: MutableMap> = concurrentMapOf() @@ -43,10 +55,20 @@ class AcquiresTypeContext { if (function.isInline) { // collect inner callExpr types val allTypes = mutableListOf() - for (innerCallExpr in inference.callableTypes.keys) { - val types = getCallTypes(innerCallExpr, inference) - allTypes.addAll(types) + + val visitor = object: AcquireTypesOwnerVisitor() { + override fun visitAcquireTypesOwner(acqTypesOwner: MvAcquireTypesOwner) { + val tys = + when (acqTypesOwner) { + is MvCallable -> getCallTypes(acqTypesOwner, inference) + is MvIndexExpr -> getIndexExprTypes(acqTypesOwner, inference) + else -> error("when is exhaustive") + } + allTypes.addAll(tys) + } } + visitor.visitElement(function) + allTypes } else { // parse from MvAcquiresType From 87ffc1ce99a8aede8580316f62ee3f517eaaa68e Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Tue, 2 Jul 2024 16:19:37 +0300 Subject: [PATCH 07/10] borrow deref into index notation quickfix --- .../annotator/fixes/WrapWithParensExprFix.kt | 3 +- .../ReplaceWithMethodCallInspection.kt | 6 +- .../ReplaceWithIndexExprInspection.kt | 52 +++ .../fixes/ReplaceWithIndexExprFix.kt | 32 ++ .../fixes/ReplaceWithMethodCallFix.kt | 4 +- .../org/move/lang/core/psi/MvPsiFactory.kt | 8 +- .../move/lang/core/psi/ext/MvMethodCall.kt | 18 +- .../move/lang/core/types/infer/TypeError.kt | 3 + .../kotlin/org/move/lang/core/types/ty/Ty.kt | 3 + .../resources/META-INF/intellij-move-core.xml | 4 + .../ReplaceWithIndexExpr.html | 5 + .../ReplaceWithMethodCall.html | 5 + .../inspections/MvTypeCheckInspectionTest.kt | 22 ++ .../ReplaceWithIndexExprInspectionTest.kt | 374 ++++++++++++++++++ 14 files changed, 522 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt create mode 100644 src/main/kotlin/org/move/ide/inspections/compilerV2/fixes/ReplaceWithIndexExprFix.kt create mode 100644 src/main/resources/inspectionDescriptions/ReplaceWithIndexExpr.html create mode 100644 src/main/resources/inspectionDescriptions/ReplaceWithMethodCall.html create mode 100644 src/test/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspectionTest.kt diff --git a/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt b/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt index cf7291393..6ba8199d7 100644 --- a/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt +++ b/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt @@ -23,7 +23,6 @@ class WrapWithParensExprFix(castExpr: MvCastExpr) : DiagnosticIntentionFix("(dummy_ident)") - parensExpr.expr?.replace(expr) + val parensExpr = psiFactory.wrapWithParens(expr) return expr.replace(parensExpr) as MvParensExpr } diff --git a/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt b/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt index e18a27220..02a500a5f 100644 --- a/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt +++ b/src/main/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspection.kt @@ -4,8 +4,8 @@ import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import org.move.ide.inspections.fixes.ReplaceWithMethodCallFix import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.getTyItemModule import org.move.lang.core.psi.ext.isMsl +import org.move.lang.core.psi.ext.itemModule import org.move.lang.core.psi.ext.valueArguments import org.move.lang.core.types.infer.foldTyTypeParameterWith import org.move.lang.core.types.infer.inference @@ -18,7 +18,7 @@ class ReplaceWithMethodCallInspection: MvLocalInspectionTool() { override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor { return object: MvVisitor() { override fun visitCallExpr(callExpr: MvCallExpr) { - val function = callExpr.path.reference?.resolve() as? MvFunction ?: return + val function = callExpr.path.reference?.resolveWithAliases() as? MvFunction ?: return val msl = callExpr.isMsl() val inference = callExpr.inference(msl) ?: return @@ -28,7 +28,7 @@ class ReplaceWithMethodCallInspection: MvLocalInspectionTool() { val moveProject = callExpr.moveProject ?: return val methodModule = function.module ?: return - val itemModule = moveProject.getTyItemModule(firstArgExprTy) ?: return + val itemModule = firstArgExprTy.itemModule(moveProject) ?: return if (methodModule != itemModule) return val selfTy = function.selfParamTy(msl) diff --git a/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt b/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt new file mode 100644 index 000000000..20127d940 --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspection.kt @@ -0,0 +1,52 @@ +package org.move.ide.inspections.compilerV2 + +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import org.move.ide.inspections.MvLocalInspectionTool +import org.move.ide.inspections.compilerV2.fixes.ReplaceWithIndexExprFix +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.argumentExprs +import org.move.lang.core.psi.ext.is0x1Address +import org.move.lang.core.psi.ext.isMsl +import org.move.lang.core.types.infer.inference +import org.move.lang.core.types.ty.isCopy +import org.move.lang.moveProject + +class ReplaceWithIndexExprInspection: MvLocalInspectionTool() { + override fun buildMvVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): MvVisitor { + return object: MvVisitor() { + override fun visitCallExpr(callExpr: MvCallExpr) { + val function = callExpr.path.reference?.resolveWithAliases() as? MvFunction ?: return + val module = function.module ?: return + val moveProject = function.moveProject ?: return + val msl = callExpr.isMsl() + val inference = callExpr.inference(msl) ?: return + val callExprRange = callExpr.textRange + // vector methods + if (inference.typeErrors.any { callExprRange.contains(it.range()) }) { + // type error inside the call expr + return + } + if (module.name == "vector" && module.is0x1Address(moveProject)) { + val parentExpr = callExpr.parent + if (function.name == "borrow" && parentExpr is MvDerefExpr) { + val receiverParamExpr = callExpr.argumentExprs.firstOrNull() ?: return + if (receiverParamExpr is MvBorrowExpr) { + val itemExpr = receiverParamExpr.expr ?: return + if (!inference.getExprType(itemExpr).isCopy) { + // cannot borrow deref without copy + return + } + } + holder.registerProblem( + parentExpr, + "Can be replaced with index expr", + ProblemHighlightType.WEAK_WARNING, + ReplaceWithIndexExprFix(parentExpr) + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/inspections/compilerV2/fixes/ReplaceWithIndexExprFix.kt b/src/main/kotlin/org/move/ide/inspections/compilerV2/fixes/ReplaceWithIndexExprFix.kt new file mode 100644 index 000000000..a7513d45b --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/compilerV2/fixes/ReplaceWithIndexExprFix.kt @@ -0,0 +1,32 @@ +package org.move.ide.inspections.compilerV2.fixes + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import org.move.ide.inspections.DiagnosticFix +import org.move.lang.core.psi.* +import org.move.lang.core.psi.ext.argExpr +import org.move.lang.core.psi.ext.argumentExprs +import org.move.lang.core.psi.ext.receiverExpr + +class ReplaceWithIndexExprFix(expr: MvExpr): DiagnosticFix(expr) { + override fun getText(): String = "Replace with index expr" + override fun invoke(project: Project, file: PsiFile, element: MvExpr) { + val derefExpr = element as? MvDerefExpr ?: return + val callExpr = derefExpr.expr as? MvCallExpr ?: return + + val receiverParamExpr = callExpr.argumentExprs.firstOrNull() ?: return + val argParamExpr = callExpr.argumentExprs.drop(1).firstOrNull() ?: return + + val indexExpr = project.psiFactory.expr("v[0]") + indexExpr.argExpr.replace(argParamExpr) + + val receiverExpr = when (receiverParamExpr) { + is MvRefExpr, is MvParensExpr -> receiverParamExpr + is MvBorrowExpr -> receiverParamExpr.expr ?: return + else -> project.psiFactory.wrapWithParens(receiverParamExpr) + } + indexExpr.receiverExpr.replace(receiverExpr) + + element.replace(indexExpr) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/ide/inspections/fixes/ReplaceWithMethodCallFix.kt b/src/main/kotlin/org/move/ide/inspections/fixes/ReplaceWithMethodCallFix.kt index 1c6f38885..c6ad2ea94 100644 --- a/src/main/kotlin/org/move/ide/inspections/fixes/ReplaceWithMethodCallFix.kt +++ b/src/main/kotlin/org/move/ide/inspections/fixes/ReplaceWithMethodCallFix.kt @@ -34,9 +34,7 @@ class ReplaceWithMethodCallFix(callExpr: MvCallExpr): DiagnosticFix( // do nothing, those operations priorities are correct without parens } else -> { - val parensExpr = psiFactory.expr("(a)") - parensExpr.expr?.replace(selfArgExpr) - selfArgExpr = parensExpr + selfArgExpr = psiFactory.wrapWithParens(selfArgExpr) } } diff --git a/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt b/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt index 2a7b7b3c2..e3df70df8 100644 --- a/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt +++ b/src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt @@ -89,6 +89,12 @@ class MvPsiFactory(val project: Project) { createFromText("module 0x1::_DummyModule { fun call() { let _ = $text; } }") ?: error("Failed to create expr") + fun wrapWithParens(expr: MvExpr): MvParensExpr { + val parensExpr = this.expr("(dummy_ident)") + parensExpr.expr?.replace(expr) + return parensExpr + } + fun type(text: String): MvType = createFromText("module 0x1::_DummyModule { fun call() { let _: $text; } }") ?: error("Failed to create type") @@ -185,7 +191,7 @@ class MvPsiFactory(val project: Project) { .createFileFromText( "$moduleName.move", MoveFileType, - "module $moduleName { $text }" + "module 0x0::$moduleName { $text }" ) as MoveFile val functions = dummyFile.childOfType()?.moduleBlock?.functionList.orEmpty() return functions diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt index 0a94e5b8d..74d861416 100644 --- a/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt +++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvMethodCall.kt @@ -23,25 +23,27 @@ fun MatchSequence.filterByName(refName: String): Sequence .map { it.element } } -fun MoveProject.getTyItemModule(ty: Ty): MvModule? { - val norefTy = ty.derefIfNeeded() +fun Ty.itemModule(moveProject: MoveProject): MvModule? { + val norefTy = this.derefIfNeeded() return when (norefTy) { is TyVector -> { - this + moveProject .getModulesFromIndex("vector") - .firstOrNull { - val moduleAddress = it.address(this)?.canonicalValue(this) - moduleAddress == "0x00000000000000000000000000000001" - } + .firstOrNull { it.is0x1Address(moveProject) } } is TyStruct -> norefTy.item.module else -> null } } +fun MvModule.is0x1Address(moveProject: MoveProject): Boolean { + val moduleAddress = this.address(moveProject)?.canonicalValue(moveProject) + return moduleAddress == "0x00000000000000000000000000000001" +} + fun getMethodVariants(element: MvMethodOrField, receiverTy: Ty, msl: Boolean): MatchSequence { val moveProject = element.moveProject ?: return emptySequence() - val receiverTyItemModule = moveProject.getTyItemModule(receiverTy) ?: return emptySequence() + val receiverTyItemModule = receiverTy.itemModule(moveProject) ?: return emptySequence() val visibilities = Visibility.publicVisibilitiesFor(element).toMutableSet() if (element.containingModule == receiverTyItemModule) { diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt index 63201e4ba..732f19d67 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeError.kt @@ -1,6 +1,7 @@ package org.move.lang.core.types.infer import com.intellij.codeInspection.LocalQuickFix +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import org.move.ide.inspections.fixes.IntegerCastFix import org.move.ide.presentation.name @@ -17,6 +18,8 @@ sealed class TypeError(open val element: PsiElement) : TypeFoldable { override fun innerVisitWith(visitor: TypeVisitor): Boolean = true + open fun range(): TextRange = element.textRange + open fun fix(): LocalQuickFix? = null data class TypeMismatch( diff --git a/src/main/kotlin/org/move/lang/core/types/ty/Ty.kt b/src/main/kotlin/org/move/lang/core/types/ty/Ty.kt index 44e8d77e2..0806db8be 100644 --- a/src/main/kotlin/org/move/lang/core/types/ty/Ty.kt +++ b/src/main/kotlin/org/move/lang/core/types/ty/Ty.kt @@ -8,6 +8,7 @@ import org.move.lang.core.types.infer.HasTypeFlagVisitor.Companion.HAS_TY_TYPE_P import org.move.lang.core.types.infer.HasTypeFlagVisitor.Companion.HAS_TY_UNKNOWN_VISITOR import org.move.lang.core.types.infer.HasTypeFlagVisitor.Companion.NEEDS_INFER import org.move.lang.core.types.infer.HasTypeFlagVisitor.Companion.NEEDS_SUBST +import org.move.lang.core.types.ty.Ability.COPY enum class Ability { DROP, COPY, STORE, KEY; @@ -32,6 +33,8 @@ enum class Ability { } } +val Ty.isCopy: Boolean get() = this.abilities().contains(COPY) + val TypeFoldable<*>.hasTyInfer get() = visitWith(HAS_TY_INFER_VISITOR) val TypeFoldable<*>.hasTyTypeParameters get() = visitWith(HAS_TY_TYPE_PARAMETER_VISITOR) val TypeFoldable<*>.hasTyStruct get() = visitWith(HAS_TY_STRUCT_VISITOR) diff --git a/src/main/resources/META-INF/intellij-move-core.xml b/src/main/resources/META-INF/intellij-move-core.xml index 590addc75..5cbb61de6 100644 --- a/src/main/resources/META-INF/intellij-move-core.xml +++ b/src/main/resources/META-INF/intellij-move-core.xml @@ -283,6 +283,10 @@ displayName="Convert to method call" enabledByDefault="true" level="WEAK WARNING" implementationClass="org.move.ide.inspections.ReplaceWithMethodCallInspection" /> + + +Detects built-in global storage calls which can be replaced with the index notation. + + \ No newline at end of file diff --git a/src/main/resources/inspectionDescriptions/ReplaceWithMethodCall.html b/src/main/resources/inspectionDescriptions/ReplaceWithMethodCall.html new file mode 100644 index 000000000..3ecbe37d3 --- /dev/null +++ b/src/main/resources/inspectionDescriptions/ReplaceWithMethodCall.html @@ -0,0 +1,5 @@ + + +Detects ability to use receiver-style function (Compiler V2 only). + + \ No newline at end of file diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index 9e379d7c9..c471a493b 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -1598,4 +1598,26 @@ module 0x1::pool { } } """) + + fun `test incorrect types for vector borrow methods`() = checkByText(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + + vector::borrow(0, &v); + vector::borrow(v, 0); + + vector::borrow_mut(0, &mut v); + vector::borrow_mut(v, 0); + vector::borrow_mut(&v, 0); + + } + } + """) } diff --git a/src/test/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspectionTest.kt new file mode 100644 index 000000000..6475d0eae --- /dev/null +++ b/src/test/kotlin/org/move/ide/inspections/compilerV2/ReplaceWithIndexExprInspectionTest.kt @@ -0,0 +1,374 @@ +package org.move.ide.inspections.compilerV2 + +import org.intellij.lang.annotations.Language +import org.move.utils.tests.FileTreeBuilder +import org.move.utils.tests.annotation.InspectionTestBase + +class ReplaceWithIndexExprInspectionTest: InspectionTestBase(ReplaceWithIndexExprInspection::class) { + + fun `test no error if types of arguments are incorrect`() = doTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + + vector::borrow(0, &v); + vector::borrow(v, 0); + + vector::borrow_mut(0, &mut v); + vector::borrow_mut(v, 0); + vector::borrow_mut(&v, 0); + } + } + """) + + fun `test no error if address is 0x2`() = doTest(""" + module 0x2::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + vector::borrow(&v, 0); + vector::borrow_mut(&mut v, 0); + } + } + """) + + fun `test no error if borrow deref is called but item is not copy`() = doTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + struct S { field: u8 } + + fun main() { + let v = vector[S { field: 10 }]; + *vector::borrow(&v, 0); + } + } + """) + + fun `test replace vector borrow deref`() = doFixTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + let vv = &v; + /*caret*/*vector::borrow(vv, 0); + } + } + """, """ + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + let vv = &v; + vv[0]; + } + } + """) + + fun `test replace vector borrow deref with reference`() = doFixTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + /*caret*/*vector::borrow(&v, 0); + } + } + """, """ + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + v[0]; + } + } + """) + + fun `test replace vector borrow deref with mut reference`() = doFixTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + /*caret*/*vector::borrow(&mut v, 0); + } + } + """, """ + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + fun main() { + let v = vector[1, 2]; + v[0]; + } + } + """) + + fun `test replace vector borrow deref with dot expr`() = doFixTest(""" + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + struct S has copy { field: u8 } + + fun main() { + let v = vector[S { field: 0 }]; + (/*caret*/*vector::borrow(&v, 0)).field; + } + } + """, """ + module 0x1::vector { + native public fun borrow(v: &vector, i: u64): ∈ + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + } + module 0x1::m { + use 0x1::vector; + + struct S has copy { field: u8 } + + fun main() { + let v = vector[S { field: 0 }]; + (v[0]).field; + } + } + """) + +// fun `test replace vector borrow`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// /*caret*/vector::borrow(&v, 0); +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// &v[0]; +// } +// } +// """) + +// fun `test replace vector borrow with variable`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// let vv = &v; +// /*caret*/vector::borrow(vv, 0); +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// let vv = &v; +// vv[0]; +// } +// } +// """) + +// fun `test replace vector borrow mut`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// /*caret*/vector::borrow_mut(&v, 0); +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// fun main() { +// let v = vector[1, 2]; +// &mut v[0]; +// } +// } +// """) + +// fun `test replace vector borrow with dot expr`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// /*caret*/vector::borrow(&v, 0).field; +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// (&v[0]).field; +// } +// } +// """) + +// fun `test replace vector borrow with dot expr with variable`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// let vv = &v; +// /*caret*/vector::borrow(vv, 0).field; +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// let vv = &v; +// vv[0].field; +// } +// } +// """) + +// fun `test replace vector borrow mut with dot expr`() = doFixTest(""" +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// /*caret*/vector::borrow_mut(&mut v, 0).field; +// } +// } +// """, """ +// module 0x1::vector { +// native public fun borrow(v: &vector, i: u64): ∈ +// native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; +// } +// module 0x1::m { +// use 0x1::vector; +// +// struct S { field: u8 } +// +// fun main() { +// let v = vector[S { field: 0 }]; +// (&mut v[0]).field; +// } +// } +// """) + + private fun doTest(@Language("Move") text: String) = + checkByText(text, checkWarn = false, checkWeakWarn = true) + + private fun doFixTest( + @Language("Move") before: String, + @Language("Move") after: String, + ) = + checkFixByText("Replace with index expr", before, after, + checkWarn = false, checkWeakWarn = true) +} \ No newline at end of file From 54e23cb5098e73c6e2e91215f000e55639da83ba Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Tue, 2 Jul 2024 16:43:11 +0300 Subject: [PATCH 08/10] fix index expr in specs --- .../core/types/infer/TypeInferenceWalker.kt | 4 +- .../inspections/MvTypeCheckInspectionTest.kt | 61 --------------- .../compilerV2/TypeCheckIndexExprTest.kt | 76 +++++++++++++++++++ .../lang/types/compilerV2/IndexExprTypes.kt | 27 +++++++ 4 files changed, 106 insertions(+), 62 deletions(-) create mode 100644 src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index d8a64df5c..6a3f0f472 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -689,7 +689,9 @@ class TypeInferenceWalker( receiverTy } else -> { - ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) + if (!ctx.msl) { + ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) + } TyUnknown } } diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index c471a493b..498afb337 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -1538,67 +1538,6 @@ module 0x1::pool { """ ) - @CompilerV2Features() - fun `test no error receiver of vector index expr without compiler v2 feature`() = checkByText(""" - module 0x1::m { - fun main() { - let b = false; - b[0]; - } - } - """) - - @CompilerV2Features(INDEXING) - fun `test error receiver of vector index expr`() = checkByText(""" - module 0x1::m { - fun main() { - let b = false; - b[0]; - } - } - """) - - @CompilerV2Features(INDEXING) - fun `test error receiver of resource index expr`() = checkByText(""" - module 0x1::m { - fun main() { - let b = false; - b[@0x1]; - } - } - """) - - @CompilerV2Features(INDEXING) - fun `test vector index expr argument is not an integer`() = checkByText(""" - module 0x1::m { - fun main() { - let v = vector[1, 2]; - v[false]; - } - } - """) - - @CompilerV2Features(INDEXING) - fun `test vector index expr argument is not an integer in spec`() = checkByText(""" - module 0x1::m { - spec fun main(): u8 { - let v = vector[1, 2]; - v[false]; - 1 - } - } - """) - - @CompilerV2Features(INDEXING) - fun `test resource index expr argument is not an address`() = checkByText(""" - module 0x1::m { - struct S has key {} - fun main() { - S[false]; - } - } - """) - fun `test incorrect types for vector borrow methods`() = checkByText(""" module 0x1::vector { native public fun borrow(v: &vector, i: u64): ∈ diff --git a/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt new file mode 100644 index 000000000..ac82b45c2 --- /dev/null +++ b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt @@ -0,0 +1,76 @@ +package org.move.ide.inspections.compilerV2 + +import org.move.ide.inspections.MvTypeCheckInspection +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features +import org.move.utils.tests.annotation.InspectionTestBase + +@CompilerV2Features(INDEXING) +class TypeCheckIndexExprTest: InspectionTestBase(MvTypeCheckInspection::class) { + + @CompilerV2Features() + fun `test no error receiver of vector index expr without compiler v2 feature`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[0]; + } + } + """) + + fun `test error receiver of vector index expr`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[0]; + } + } + """) + + fun `test error receiver of resource index expr`() = checkByText(""" + module 0x1::m { + fun main() { + let b = false; + b[@0x1]; + } + } + """) + + fun `test vector index expr argument is not an integer`() = checkByText(""" + module 0x1::m { + fun main() { + let v = vector[1, 2]; + v[false]; + } + } + """) + + fun `test vector index expr argument is not an integer in spec`() = checkByText(""" + module 0x1::m { + spec fun main(): u8 { + let v = vector[1, 2]; + v[false]; + 1 + } + } + """) + + fun `test resource index expr argument is not an address`() = checkByText(""" + module 0x1::m { + struct S has key {} + fun main() { + S[false]; + } + } + """) + + fun `test no error for vector reference inside specs`() = checkByText(""" + module 0x1::m { + fun main() {} + spec main { + let v = vector[1, 2]; + (&v)[1]; + } + } + """) +} \ No newline at end of file diff --git a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt index 0170dd7e3..0a100745d 100644 --- a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt +++ b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt @@ -364,4 +364,31 @@ class IndexExprTypes: TypificationTestCase() { } } """) + + fun `test index expr with reference in specs`() = testExpr(""" + module 0x1::m { + struct S { field: u8 } + fun main() {} + spec main { + let v = vector[S { field: 10 }]; + let vv = &v; + vv[0].field; + //^ num + } + } + """) + + @CompilerV2Features() + fun `test index expr with reference in specs without compiler v2`() = testExpr(""" + module 0x1::m { + struct S { field: u8 } + fun main() {} + spec main { + let v = vector[S { field: 10 }]; + let vv = &v; + vv[0].field; + //^ num + } + } + """) } \ No newline at end of file From c49f019908f7b182617110e63d54a484cb9b66c5 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Tue, 2 Jul 2024 19:39:52 +0300 Subject: [PATCH 09/10] index expr fixes --- .../settings/PerProjectAptosConfigurable.kt | 1 + .../move/lang/core/types/infer/Acquires.kt | 6 +-- .../core/types/infer/TypeInferenceWalker.kt | 15 +++---- .../MvUnusedAcquiresTypeInspectionTest.kt | 36 +++++++++++++++ ...laceWithMethodCallInspectionProjectTest.kt | 40 +++++++++++++++++ .../ReplaceWithMethodCallInspectionTest.kt | 2 + .../compilerV2/TypeCheckIndexExprTest.kt | 45 ++++++++++++------- 7 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt b/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt index bd8aaef2b..7bf151a5a 100644 --- a/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt +++ b/src/main/kotlin/org/move/cli/settings/PerProjectAptosConfigurable.kt @@ -110,6 +110,7 @@ class PerProjectAptosConfigurable(val project: Project): BoundConfigurable("Apto it.skipFetchLatestGitDeps = state.skipFetchLatestGitDeps it.dumpStateOnTestFailure = state.dumpStateOnTestFailure it.enableResourceAccessControl = state.enableResourceAccessControl + it.enableIndexExpr = state.enableIndexExpr it.addCompilerV2CLIFlags = state.addCompilerV2CLIFlags it.fetchAptosDeps = state.fetchAptosDeps } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt index 5525d2831..6b1be6951 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/Acquires.kt @@ -38,10 +38,10 @@ abstract class AcquireTypesOwnerVisitor: PsiRecursiveElementVisitor() { abstract fun visitAcquireTypesOwner(acqTypesOwner: MvAcquireTypesOwner) override fun visitElement(element: PsiElement) { - when (element) { - is MvAcquireTypesOwner -> visitAcquireTypesOwner(element) - else -> super.visitElement(element) + if (element is MvAcquireTypesOwner) { + visitAcquireTypesOwner(element) } + super.visitElement(element) } } diff --git a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt index 6a3f0f472..c1a345bba 100644 --- a/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt +++ b/src/main/kotlin/org/move/lang/core/types/infer/TypeInferenceWalker.kt @@ -672,26 +672,25 @@ class TypeInferenceWalker( return TyUnknown } - return when (receiverTy) { - is TyVector -> { + val derefTy = receiverTy.derefIfNeeded() + return when { + derefTy is TyVector -> { // argExpr can be either TyInteger or TyRange when (argTy) { - is TyRange -> receiverTy - is TyInteger, is TyInfer.IntVar, is TyNum -> receiverTy.item + is TyRange -> derefTy + is TyInteger, is TyInfer.IntVar, is TyNum -> derefTy.item else -> { coerce(indexExpr.argExpr, argTy, if (ctx.msl) TyNum else TyInteger.DEFAULT) TyUnknown } } } - is TyStruct -> { + receiverTy is TyStruct -> { coerce(indexExpr.argExpr, argTy, TyAddress) receiverTy } else -> { - if (!ctx.msl) { - ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) - } + ctx.reportTypeError(TypeError.IndexingIsNotAllowed(indexExpr.receiverExpr, receiverTy)) TyUnknown } } diff --git a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt index 2db91a57c..d799be557 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt @@ -190,6 +190,42 @@ module 0x1::main { inline fun f() { StakePool[pool_address]; } +} + """ + ) + + fun `test no unused acquires for deep borrow global dot`() = checkWarnings( + """ +module 0x1::main { + /// The enabled features, represented by a bitset stored on chain. + struct Features has key { + features: vector, + } + + #[view] + /// Check whether the feature is enabled. + public fun is_enabled(feature: u64): bool acquires Features { + exists(@0x1) && + contains(&borrow_global(@0x1).features, feature) + } +} + """ + ) + + @CompilerV2Features(INDEXING) + fun `test no unused acquires for deep borrow global dot with index expr`() = checkWarnings( + """ +module 0x1::main { + /// The enabled features, represented by a bitset stored on chain. + struct Features has key { + features: vector, + } + #[view] + /// Check whether the feature is enabled. + public fun is_enabled(feature: u64): bool acquires Features { + exists(@0x1) && + contains(Features[@0x1].features, feature) + } } """ ) diff --git a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt index 9823886ca..ecc144722 100644 --- a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt @@ -1,6 +1,8 @@ package org.move.ide.inspections import org.intellij.lang.annotations.Language +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.FileTreeBuilder import org.move.utils.tests.annotation.InspectionProjectTestBase @@ -108,6 +110,44 @@ class ReplaceWithMethodCallInspectionProjectTest: } """) + @CompilerV2Features(INDEXING) + fun `test replace with method call with index expr`() = doFixTest( + { + namedMoveToml("MyPackage") + sources { + move( + "vector.move", """ + module 0x1::vector { + #[bytecode_instruction] + /// Add element `e` to the end of the vector `v`. + native public fun push_back(self: &mut vector, e: Element); + } + """ + ) + main( + """ + module 0x1::m { + use 0x1::vector::push_back; + fun main() { + let v = vector[1, 2]; + let vec = vector[]; + /*caret*/push_back(&mut vec, v[0]); + } + } + """ + ) + } + }, """ + module 0x1::m { + use 0x1::vector::push_back; + fun main() { + let v = vector[1, 2]; + let vec = vector[]; + vec.push_back(v[0]); + } + } + """) + private fun doTest(code: FileTreeBuilder.() -> Unit) = checkByFileTree(code, checkWarn = false, checkWeakWarn = true) diff --git a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt index 083217fa1..f5539962b 100644 --- a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt @@ -1,6 +1,8 @@ package org.move.ide.inspections import org.intellij.lang.annotations.Language +import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class ReplaceWithMethodCallInspectionTest: InspectionTestBase(ReplaceWithMethodCallInspection::class) { diff --git a/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt index ac82b45c2..ddb01d92a 100644 --- a/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt @@ -9,43 +9,52 @@ import org.move.utils.tests.annotation.InspectionTestBase class TypeCheckIndexExprTest: InspectionTestBase(MvTypeCheckInspection::class) { @CompilerV2Features() - fun `test no error receiver of vector index expr without compiler v2 feature`() = checkByText(""" + fun `test no error receiver of vector index expr without compiler v2 feature`() = checkByText( + """ module 0x1::m { fun main() { let b = false; b[0]; } } - """) + """ + ) - fun `test error receiver of vector index expr`() = checkByText(""" + fun `test error receiver of vector index expr`() = checkByText( + """ module 0x1::m { fun main() { let b = false; b[0]; } } - """) + """ + ) - fun `test error receiver of resource index expr`() = checkByText(""" + fun `test error receiver of resource index expr`() = checkByText( + """ module 0x1::m { fun main() { let b = false; b[@0x1]; } } - """) + """ + ) - fun `test vector index expr argument is not an integer`() = checkByText(""" + fun `test vector index expr argument is not an integer`() = checkByText( + """ module 0x1::m { fun main() { let v = vector[1, 2]; v[false]; } } - """) + """ + ) - fun `test vector index expr argument is not an integer in spec`() = checkByText(""" + fun `test vector index expr argument is not an integer in spec`() = checkByText( + """ module 0x1::m { spec fun main(): u8 { let v = vector[1, 2]; @@ -53,24 +62,28 @@ class TypeCheckIndexExprTest: InspectionTestBase(MvTypeCheckInspection::class) { 1 } } - """) + """ + ) - fun `test resource index expr argument is not an address`() = checkByText(""" + fun `test resource index expr argument is not an address`() = checkByText( + """ module 0x1::m { struct S has key {} fun main() { S[false]; } } - """) + """ + ) - fun `test no error for vector reference inside specs`() = checkByText(""" + fun `test no error for vector reference`() = checkByText( + """ module 0x1::m { - fun main() {} - spec main { + fun main() { let v = vector[1, 2]; (&v)[1]; } } - """) + """ + ) } \ No newline at end of file From 4eb5ac0635a5981c6732bad532eb157a696a8af2 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 3 Jul 2024 15:05:05 +0300 Subject: [PATCH 10/10] highlight not supported index expr in compiler v1 with quickfix --- .../ide/annotator/MvSyntaxErrorAnnotator.kt | 11 +++++ .../annotator/fixes/WrapWithParensExprFix.kt | 1 + .../ide/inspections/MvLocalInspectionTool.kt | 6 +++ .../fixes/EnableCompilerV2FeatureFix.kt | 38 ++++++++++++++++++ .../kotlin/org/move/lang/utils/Diagnostic.kt | 13 ++++++ .../kotlin/org/move/utils/tests/MvTestBase.kt | 11 ++--- .../annotator/HighlightingAnnotatorTest.kt | 2 +- .../syntaxErrors/compilerV2/IndexExprTest.kt | 40 +++++++++++++++++++ .../MvMissingAcquiresInspectionTest.kt | 2 +- .../inspections/MvTypeCheckInspectionTest.kt | 2 - .../MvUnusedAcquiresTypeInspectionTest.kt | 2 +- ...laceWithMethodCallInspectionProjectTest.kt | 2 +- .../ReplaceWithMethodCallInspectionTest.kt | 2 - .../compilerV2/TypeCheckIndexExprTest.kt | 2 +- .../lang/completion/KeywordCompletionTest.kt | 2 +- .../move/lang/parser/CompleteParsingTest.kt | 2 +- .../ResolveResourceAccessSpecifiersTest.kt | 2 +- .../org/move/lang/resolve/ResolveTypesTest.kt | 2 +- .../lang/types/ExpressionTypeInferenceTest.kt | 2 - .../lang/types/compilerV2/IndexExprTypes.kt | 2 +- 20 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 src/main/kotlin/org/move/ide/inspections/fixes/EnableCompilerV2FeatureFix.kt create mode 100644 src/test/kotlin/org/move/ide/annotator/syntaxErrors/compilerV2/IndexExprTest.kt diff --git a/src/main/kotlin/org/move/ide/annotator/MvSyntaxErrorAnnotator.kt b/src/main/kotlin/org/move/ide/annotator/MvSyntaxErrorAnnotator.kt index 1f2156709..f999b7d80 100644 --- a/src/main/kotlin/org/move/ide/annotator/MvSyntaxErrorAnnotator.kt +++ b/src/main/kotlin/org/move/ide/annotator/MvSyntaxErrorAnnotator.kt @@ -3,6 +3,7 @@ package org.move.ide.annotator import com.intellij.lang.annotation.AnnotationHolder import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement +import org.move.cli.settings.moveSettings import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* import org.move.lang.utils.Diagnostic @@ -20,6 +21,7 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() { override fun visitStruct(s: MvStruct) = checkStruct(moveHolder, s) override fun visitFunction(o: MvFunction) = checkFunction(moveHolder, o) override fun visitSpecFunction(o: MvSpecFunction) = checkSpecFunction(moveHolder, o) + override fun visitIndexExpr(o: MvIndexExpr) = checkIndexExpr(moveHolder, o) } element.accept(visitor) } @@ -56,6 +58,14 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() { } } + private fun checkIndexExpr(holder: MvAnnotationHolder, indexExpr: MvIndexExpr) { + if (!indexExpr.project.moveSettings.enableIndexExpr) { + Diagnostic + .IndexExprIsNotAllowed(indexExpr) + .addToHolder(holder) + } + } + private fun checkStruct(holder: MvAnnotationHolder, struct: MvStruct) { val native = struct.native ?: return val errorRange = TextRange.create(native.startOffset, struct.structKw.endOffset) @@ -154,6 +164,7 @@ class MvSyntaxErrorAnnotator: MvAnnotatorBase() { } } + @Suppress("CompanionObjectInExtension") companion object { private val INTEGER_WITH_SUFFIX_REGEX = Regex("([0-9a-zA-Z_]+)(u[0-9]{1,4})") diff --git a/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt b/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt index 6ba8199d7..847a12650 100644 --- a/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt +++ b/src/main/kotlin/org/move/ide/annotator/fixes/WrapWithParensExprFix.kt @@ -10,6 +10,7 @@ import org.move.lang.core.psi.* class WrapWithParensExprFix(castExpr: MvCastExpr) : DiagnosticIntentionFix(castExpr) { override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY + override fun getFamilyName(): String = "Wrap cast with ()" override fun getText(): String = "Wrap cast with ()" diff --git a/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt b/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt index 71100a487..c7931c6d2 100644 --- a/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt +++ b/src/main/kotlin/org/move/ide/inspections/MvLocalInspectionTool.kt @@ -2,6 +2,9 @@ package org.move.ide.inspections import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInspection.* +import com.intellij.model.SideEffectGuard +import com.intellij.model.SideEffectGuard.EffectType.EXEC +import com.intellij.model.SideEffectGuard.EffectType.SETTINGS import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement @@ -107,6 +110,9 @@ abstract class DiagnosticIntentionFix(element: T) : override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY + override fun generatePreview(project: Project, editor: Editor, file: PsiFile): IntentionPreviewInfo = + IntentionPreviewInfo.EMPTY + override fun getFamilyName(): String = text override fun isAvailable( diff --git a/src/main/kotlin/org/move/ide/inspections/fixes/EnableCompilerV2FeatureFix.kt b/src/main/kotlin/org/move/ide/inspections/fixes/EnableCompilerV2FeatureFix.kt new file mode 100644 index 000000000..0a0a479d8 --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/fixes/EnableCompilerV2FeatureFix.kt @@ -0,0 +1,38 @@ +package org.move.ide.inspections.fixes + +import com.intellij.model.SideEffectGuard +import com.intellij.model.SideEffectGuard.EffectType.SETTINGS +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import io.ktor.http.* +import org.move.cli.settings.moveSettings +import org.move.ide.inspections.DiagnosticIntentionFix +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL + +enum class CompilerV2Feat(val title: String) { + INDEXING("Index notation"), + RESOURCE_CONTROL("Resource access control"); +} + +class EnableCompilerV2FeatureFix( + element: PsiElement, + val feature: CompilerV2Feat +): + DiagnosticIntentionFix(element) { + + override fun getText(): String = + "Enable ${feature.title.quote()} feature of Aptos Move V2 Compiler in the plugin settings" + + override fun invoke(project: Project, file: PsiFile, element: PsiElement) { + @Suppress("UnstableApiUsage") + SideEffectGuard.checkSideEffectAllowed(SETTINGS) + project.moveSettings.modify { + when (feature) { + INDEXING -> it.enableIndexExpr = true + RESOURCE_CONTROL -> it.enableResourceAccessControl = true + } + } + } +} \ 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 11b30bfa1..25fb508b4 100644 --- a/src/main/kotlin/org/move/lang/utils/Diagnostic.kt +++ b/src/main/kotlin/org/move/lang/utils/Diagnostic.kt @@ -13,6 +13,8 @@ import org.move.ide.annotator.MvAnnotationHolder import org.move.ide.annotator.fixes.ItemSpecSignatureFix import org.move.ide.annotator.fixes.WrapWithParensExprFix import org.move.ide.annotator.pluralise +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.EnableCompilerV2FeatureFix import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.endOffset import org.move.lang.core.psi.ext.itemSpecBlock @@ -170,6 +172,17 @@ sealed class Diagnostic( ) } } + + class IndexExprIsNotAllowed(indexExpr: MvIndexExpr): Diagnostic(indexExpr) { + + override fun prepare(): PreparedAnnotation { + return PreparedAnnotation( + ERROR, + "Index operator is not supported in Aptos Move V1 outside specs", + fixes = listOf(EnableCompilerV2FeatureFix(element, INDEXING)) + ) + } + } } enum class Severity { diff --git a/src/main/kotlin/org/move/utils/tests/MvTestBase.kt b/src/main/kotlin/org/move/utils/tests/MvTestBase.kt index 39bd1f39f..ec762b4ba 100644 --- a/src/main/kotlin/org/move/utils/tests/MvTestBase.kt +++ b/src/main/kotlin/org/move/utils/tests/MvTestBase.kt @@ -7,15 +7,14 @@ package org.move.utils.tests import com.intellij.codeInspection.InspectionProfileEntry import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import com.intellij.psi.PsiElement import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.enableInspectionTool import org.intellij.lang.annotations.Language -import org.move.cli.settings.MvProjectSettingsService import org.move.cli.settings.moveSettings -import org.move.utils.tests.CompilerV2Feat.INDEXING -import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.ide.inspections.fixes.CompilerV2Feat +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.base.MvTestCase import org.move.utils.tests.base.TestCase import org.move.utils.tests.base.findElementsWithDataAndOffsetInEditor @@ -33,10 +32,6 @@ annotation class DebugMode(val enabled: Boolean) @Retention(AnnotationRetention.RUNTIME) annotation class WithEnabledInspections(vararg val inspections: KClass) -enum class CompilerV2Feat { - INDEXING, RESOURCE_CONTROL; -} - @Inherited @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) diff --git a/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt b/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt index c110353fe..f8e37fa08 100644 --- a/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt +++ b/src/test/kotlin/org/move/ide/annotator/HighlightingAnnotatorTest.kt @@ -1,7 +1,7 @@ package org.move.ide.annotator import org.move.ide.colors.MvColor -import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.AnnotatorTestCase diff --git a/src/test/kotlin/org/move/ide/annotator/syntaxErrors/compilerV2/IndexExprTest.kt b/src/test/kotlin/org/move/ide/annotator/syntaxErrors/compilerV2/IndexExprTest.kt new file mode 100644 index 000000000..acfcb260c --- /dev/null +++ b/src/test/kotlin/org/move/ide/annotator/syntaxErrors/compilerV2/IndexExprTest.kt @@ -0,0 +1,40 @@ +package org.move.ide.annotator.syntaxErrors.compilerV2 + +import org.move.ide.annotator.MvSyntaxErrorAnnotator +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING +import org.move.utils.tests.CompilerV2Features +import org.move.utils.tests.annotation.AnnotatorTestCase + +class IndexExprTest: AnnotatorTestCase(MvSyntaxErrorAnnotator::class) { + @CompilerV2Features() + fun `test vector index expr in not allowed in main code with compiler v1`() = checkWarnings(""" + module 0x1::m { + fun main() { + let v = vector[1, 2]; + v[1]; + } + } + """) + + @CompilerV2Features() + fun `test resource index expr in not allowed in main code with compiler v1`() = checkWarnings(""" + module 0x1::m { + fun main() { + let v = vector[1, 2]; + v[1]; + } + } + """) + + @CompilerV2Features(INDEXING) + fun `test no error with index expr in compiler v2`() = checkWarnings(""" + module 0x1::m { + struct S has key { field: u8 } + fun main() { + let v1 = vector[1, 2]; + v1[1]; + S[@0x1].field = 11; + } + } + """) +} \ No newline at end of file diff --git a/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt index 7d503351d..e9277ee20 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvMissingAcquiresInspectionTest.kt @@ -1,6 +1,6 @@ package org.move.ide.inspections -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase diff --git a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt index 498afb337..4b657b937 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvTypeCheckInspectionTest.kt @@ -1,7 +1,5 @@ package org.move.ide.inspections -import org.move.utils.tests.CompilerV2Feat.INDEXING -import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class MvTypeCheckInspectionTest: InspectionTestBase(MvTypeCheckInspection::class) { diff --git a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt index d799be557..01852e8ad 100644 --- a/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/MvUnusedAcquiresTypeInspectionTest.kt @@ -1,6 +1,6 @@ package org.move.ide.inspections -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase diff --git a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt index ecc144722..14782ca85 100644 --- a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionProjectTest.kt @@ -1,7 +1,7 @@ package org.move.ide.inspections import org.intellij.lang.annotations.Language -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.FileTreeBuilder import org.move.utils.tests.annotation.InspectionProjectTestBase diff --git a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt index f5539962b..083217fa1 100644 --- a/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/ReplaceWithMethodCallInspectionTest.kt @@ -1,8 +1,6 @@ package org.move.ide.inspections import org.intellij.lang.annotations.Language -import org.move.utils.tests.CompilerV2Feat.INDEXING -import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase class ReplaceWithMethodCallInspectionTest: InspectionTestBase(ReplaceWithMethodCallInspection::class) { diff --git a/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt index ddb01d92a..6956ccd54 100644 --- a/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt +++ b/src/test/kotlin/org/move/ide/inspections/compilerV2/TypeCheckIndexExprTest.kt @@ -1,7 +1,7 @@ package org.move.ide.inspections.compilerV2 import org.move.ide.inspections.MvTypeCheckInspection -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.annotation.InspectionTestBase diff --git a/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt b/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt index f7d2302fc..09a5d344b 100644 --- a/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt +++ b/src/test/kotlin/org/move/lang/completion/KeywordCompletionTest.kt @@ -1,6 +1,6 @@ package org.move.lang.completion -import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.completion.CompletionTestCase diff --git a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt index 03e0d2e0d..b02f17881 100644 --- a/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt +++ b/src/test/kotlin/org/move/lang/parser/CompleteParsingTest.kt @@ -1,6 +1,6 @@ package org.move.lang.parser -import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.parser.MvParsingTestCase diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt index 49c4a9b55..fecdc5029 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveResourceAccessSpecifiersTest.kt @@ -1,6 +1,6 @@ package org.move.lang.resolve -import org.move.utils.tests.CompilerV2Feat.RESOURCE_CONTROL +import org.move.ide.inspections.fixes.CompilerV2Feat.RESOURCE_CONTROL import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.resolve.ResolveTestCase diff --git a/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt b/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt index 6fadbe2a1..bedd9d33e 100644 --- a/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt +++ b/src/test/kotlin/org/move/lang/resolve/ResolveTypesTest.kt @@ -1,6 +1,6 @@ package org.move.lang.resolve -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.resolve.ResolveTestCase diff --git a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt index 7d2e94a7c..055e50d31 100644 --- a/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt +++ b/src/test/kotlin/org/move/lang/types/ExpressionTypeInferenceTest.kt @@ -1,7 +1,5 @@ package org.move.lang.types -import org.move.utils.tests.CompilerV2Feat.INDEXING -import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.types.TypificationTestCase class ExpressionTypeInferenceTest: TypificationTestCase() { diff --git a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt index 0a100745d..f3a2e0dae 100644 --- a/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt +++ b/src/test/kotlin/org/move/lang/types/compilerV2/IndexExprTypes.kt @@ -1,6 +1,6 @@ package org.move.lang.types.compilerV2 -import org.move.utils.tests.CompilerV2Feat.INDEXING +import org.move.ide.inspections.fixes.CompilerV2Feat.INDEXING import org.move.utils.tests.CompilerV2Features import org.move.utils.tests.types.TypificationTestCase