Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Handle invalid index-expressions in view assignment #3539

Merged
merged 1 commit into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@

elaboration := interpreter.Program.Elaboration

indexExpressionTypes := elaboration.IndexExpressionTypes(indexExpression)
indexExpressionTypes, ok := elaboration.IndexExpressionTypes(indexExpression)
if !ok {
panic(errors.NewUnreachableError())

Check warning on line 153 in runtime/interpreter/interpreter_expression.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/interpreter_expression.go#L153

Added line #L153 was not covered by tests
}

indexedType := indexExpressionTypes.IndexedType
indexingType := indexExpressionTypes.IndexingType

Expand Down Expand Up @@ -1096,7 +1100,7 @@
expression *ast.IndexExpression,
memberValue Value,
) Value {
indexExpressionTypes := interpreter.Program.Elaboration.IndexExpressionTypes(expression)
indexExpressionTypes, _ := interpreter.Program.Elaboration.IndexExpressionTypes(expression)

if indexExpressionTypes.ReturnReference {
expectedType := indexExpressionTypes.ResultType
Expand Down
14 changes: 12 additions & 2 deletions runtime/sema/check_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ func (checker *Checker) rootOfAccessChain(target ast.Expression) (baseVariable *
inAccessChain = false
case *ast.IndexExpression:
target = targetExp.TargetExpression
elementType := checker.Elaboration.IndexExpressionTypes(targetExp).IndexedType.ElementType(true)
indexExprTypes, ok := checker.Elaboration.IndexExpressionTypes(targetExp)
var elementType Type
if !ok {
elementType = InvalidType
} else {
elementType = indexExprTypes.IndexedType.ElementType(true)
}
accessChain = append(accessChain, elementType)
case *ast.MemberExpression:
target = targetExp.Expression
Expand Down Expand Up @@ -361,7 +367,11 @@ func (checker *Checker) visitIndexExpressionAssignment(
return checker.visitIndexExpression(indexExpression, true)
})

indexExprTypes := checker.Elaboration.IndexExpressionTypes(indexExpression)
indexExprTypes, ok := checker.Elaboration.IndexExpressionTypes(indexExpression)
if !ok {
return InvalidType
}

indexedRefType, isReference := MaybeReferenceType(indexExprTypes.IndexedType)

if isReference &&
Expand Down
5 changes: 4 additions & 1 deletion runtime/sema/check_composite_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,10 @@ func (checker *Checker) checkDefaultDestroyParamExpressionKind(
checker.checkDefaultDestroyParamExpressionKind(arg.TargetExpression, containerDeclarationKind)
checker.checkDefaultDestroyParamExpressionKind(arg.IndexingExpression, containerDeclarationKind)

indexExprType := checker.Elaboration.IndexExpressionTypes(arg)
indexExprType, ok := checker.Elaboration.IndexExpressionTypes(arg)
if !ok {
return
}

// indexing expressions on arrays can fail, and must be disallowed, but
// indexing expressions on dicts, or composites (for attachments) will return `nil` and thus never fail
Expand Down
6 changes: 2 additions & 4 deletions runtime/sema/check_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,14 @@ func (checker *Checker) VisitSwapStatement(swap *ast.SwapStatement) (_ struct{})

for _, side := range []ast.Expression{swap.Left, swap.Right} {
if indexExpression, ok := side.(*ast.IndexExpression); ok {
indexExpressionTypes := checker.Elaboration.IndexExpressionTypes(indexExpression)
indexExpressionTypes, ok := checker.Elaboration.IndexExpressionTypes(indexExpression)

// If the indexed type is a resource type,
// then the target expression must be considered as a nested resource move expression.
//
// The index expression might have been invalid,
// so the indexed type might be unavailable.

indexedType := indexExpressionTypes.IndexedType
if indexedType != nil && indexedType.IsResourceType() {
if ok && indexExpressionTypes.IndexedType.IsResourceType() {
targetExpression := indexExpression.TargetExpression
checker.elaborateNestedResourceMoveExpression(targetExpression)
}
Expand Down
6 changes: 4 additions & 2 deletions runtime/sema/elaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -884,11 +884,13 @@ func (e *Elaboration) SetReferenceExpressionBorrowType(expression *ast.Reference
e.referenceExpressionBorrowTypes[expression] = ty
}

func (e *Elaboration) IndexExpressionTypes(expression *ast.IndexExpression) (types IndexExpressionTypes) {
func (e *Elaboration) IndexExpressionTypes(expression *ast.IndexExpression) (types IndexExpressionTypes, contains bool) {
if e.indexExpressionTypes == nil {
return
}
return e.indexExpressionTypes[expression]

types, contains = e.indexExpressionTypes[expression]
return
}

func (e *Elaboration) SetIndexExpressionTypes(expression *ast.IndexExpression, types IndexExpressionTypes) {
Expand Down
33 changes: 33 additions & 0 deletions runtime/tests/checker/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,3 +598,36 @@ func TestCheckResultVariable(t *testing.T) {
require.NoError(t, err)
})
}

func TestCheckViewFunctionWithErrors(t *testing.T) {

t.Parallel()

t.Run("index assignment", func(t *testing.T) {
t.Parallel()

_, err := ParseAndCheck(t, `
view fun foo() {
a[b] = 1
}`,
)

errs := RequireCheckerErrors(t, err, 2)
assert.IsType(t, &sema.NotDeclaredError{}, errs[0])
assert.IsType(t, &sema.PurityError{}, errs[1])
})

t.Run("member assignment", func(t *testing.T) {
t.Parallel()

_, err := ParseAndCheck(t, `
view fun foo() {
a.b = 1
}`,
)

errs := RequireCheckerErrors(t, err, 2)
assert.IsType(t, &sema.NotDeclaredError{}, errs[0])
assert.IsType(t, &sema.PurityError{}, errs[1])
})
}
Loading