From 1dd16b8ee485efb10b51789fe68d72a843360b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20Mu=CC=88ller?= Date: Thu, 10 Feb 2022 13:52:50 -0800 Subject: [PATCH] fix resource field nesting check for enum raw value field --- runtime/sema/check_composite_declaration.go | 2 +- runtime/sema/check_interface_declaration.go | 2 +- runtime/sema/checker.go | 14 +++++- runtime/sema/type.go | 18 ++++++++ runtime/tests/checker/resources_test.go | 49 +++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index e0d52b9297..098f8fe213 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -131,7 +131,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl } fieldPositionGetter := func(name string) ast.Position { - return declaration.Members.FieldPosition(name, declaration.CompositeKind) + return compositeType.FieldPosition(name, declaration) } checker.checkResourceFieldNesting( diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 6839380662..6946829973 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -87,7 +87,7 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl ) fieldPositionGetter := func(name string) ast.Position { - return declaration.Members.FieldPosition(name, declaration.CompositeKind) + return interfaceType.FieldPosition(name, declaration) } checker.checkResourceFieldNesting( diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 1da801dd81..413541382d 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -1733,8 +1733,8 @@ func (checker *Checker) checkResourceFieldNesting( return } - // The field is not a resource or contract, check if there are - // any fields that have a resource type and report them + // The field is not a resource or contract. + // Check if there are any fields that have a resource type and report them members.Foreach(func(name string, member *Member) { // NOTE: check type, not resource annotation: @@ -1744,6 +1744,16 @@ func (checker *Checker) checkResourceFieldNesting( return } + // Skip enums' implicit rawValue field. + // If a resource type is used as the enum raw type, + // it is already reported + + if compositeKind == common.CompositeKindEnum && + name == EnumRawValueFieldName { + + return + } + pos := fieldPositionGetter(name) checker.report( diff --git a/runtime/sema/type.go b/runtime/sema/type.go index bbc5ef6ce5..321ab5f5c0 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3854,6 +3854,20 @@ func (t *CompositeType) initializeMemberResolvers() { }) } +func (t *CompositeType) FieldPosition(name string, declaration *ast.CompositeDeclaration) ast.Position { + var pos ast.Position + if t.Kind == common.CompositeKindEnum && + name == EnumRawValueFieldName { + + if len(declaration.Conformances) > 0 { + pos = declaration.Conformances[0].StartPosition() + } + } else { + pos = declaration.Members.FieldPosition(name, declaration.CompositeKind) + } + return pos +} + // Member type Member struct { @@ -4217,6 +4231,10 @@ func (t *InterfaceType) GetNestedTypes() *StringTypeOrderedMap { return t.nestedTypes } +func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDeclaration) ast.Position { + return declaration.Members.FieldPosition(name, declaration.CompositeKind) +} + // DictionaryType consists of the key and value type // for all key-value pairs in the dictionary: // All keys have to be a subtype of the key type, diff --git a/runtime/tests/checker/resources_test.go b/runtime/tests/checker/resources_test.go index dad5fb7468..6157985efa 100644 --- a/runtime/tests/checker/resources_test.go +++ b/runtime/tests/checker/resources_test.go @@ -3063,6 +3063,55 @@ func TestCheckInvalidContractResourceFieldMove(t *testing.T) { assert.IsType(t, &sema.InvalidNestedResourceMoveError{}, errs[0]) } +func TestCheckInvalidEnumResourceField(t *testing.T) { + + t.Parallel() + + t.Run("raw type given", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R {} + + enum E: R {} + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.InvalidEnumRawTypeError{}, errs[0]) + }) + + t.Run("raw type given, nested", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R { + enum E: R {} + } + `) + + errs := ExpectCheckerErrors(t, err, 2) + + require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + require.IsType(t, &sema.InvalidEnumRawTypeError{}, errs[1]) + }) + + t.Run("raw type not given", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + enum E {} + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.MissingEnumRawTypeError{}, errs[0]) + }) +} + // TestCheckResourceInterfaceConformance tests the check // of conformance of resources to resource interfaces. //