Skip to content

Commit

Permalink
Merge pull request #2665 from onflow/sainati/error-positioning
Browse files Browse the repository at this point in the history
Fix positioning of default function conflict errors
  • Loading branch information
dsainati1 authored Jul 19, 2023
2 parents 24ca444 + 1e282eb commit 181924e
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 17 deletions.
259 changes: 259 additions & 0 deletions runtime/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ import (

"github.com/stretchr/testify/require"

"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
"github.com/onflow/cadence/runtime/stdlib"
)

func TestRuntimeError(t *testing.T) {
Expand Down Expand Up @@ -493,3 +496,259 @@ func TestRuntimeError(t *testing.T) {

})
}

func TestRuntimeDefaultFunctionConflictPrintingError(t *testing.T) {
t.Parallel()

runtime := newTestInterpreterRuntime()

makeDeployTransaction := func(name, code string) []byte {
return []byte(fmt.Sprintf(
`
transaction {
prepare(signer: AuthAccount) {
let acct = AuthAccount(payer: signer)
acct.contracts.add(name: "%s", code: "%s".decodeHex())
}
}
`,
name,
hex.EncodeToString([]byte(code)),
))
}

contractInterfaceCode := `
access(all) contract TestInterfaces {
access(all) resource interface A {
access(all) fun foo() {
let x = 3
}
}
access(all) resource interface B {
access(all) fun foo()
}
}
`

contractCode := `
import TestInterfaces from 0x2
access(all) contract TestContract {
access(all) resource R: TestInterfaces.A, TestInterfaces.B {}
// fill space
// fill space
// fill space
// fill space
// fill space
// fill space
// filling lots of space
// filling lots of space
// filling lots of space
}
`

accountCodes := map[Location][]byte{}
var events []cadence.Event

var nextAccount byte = 0x2

runtimeInterface := &testRuntimeInterface{
getCode: func(location Location) (bytes []byte, err error) {
return accountCodes[location], nil
},
storage: newTestLedger(nil, nil),
createAccount: func(payer Address) (address Address, err error) {
result := interpreter.NewUnmeteredAddressValueFromBytes([]byte{nextAccount})
nextAccount++
return result.ToAddress(), nil
},
getSigningAccounts: func() ([]Address, error) {
return []Address{{0x1}}, nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
return accountCodes[location], nil
},
updateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
emitEvent: func(event cadence.Event) error {
events = append(events, event)
return nil
},
}

nextTransactionLocation := newTransactionLocationGenerator()

deployTransaction := makeDeployTransaction("TestInterfaces", contractInterfaceCode)
err := runtime.ExecuteTransaction(
Script{
Source: deployTransaction,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

deployTransaction = makeDeployTransaction("TestContract", contractCode)
err = runtime.ExecuteTransaction(
Script{
Source: deployTransaction,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
require.Error(t, err)
require.Contains(t, err.Error(), "access(all) resource R: TestInterfaces.A, TestInterfaces.B {}")

var errType *sema.CheckerError
require.ErrorAs(t, err, &errType)

checkerErr := err.(Error).
Err.(interpreter.Error).
Err.(*stdlib.InvalidContractDeploymentError).
Err.(*ParsingCheckingError).
Err.(*sema.CheckerError)

var specificErrType *sema.DefaultFunctionConflictError
require.ErrorAs(t, checkerErr.Errors[0], &specificErrType)

errorRange := checkerErr.Errors[0].(*sema.DefaultFunctionConflictError).Range

require.Equal(t, errorRange.StartPos.Line, 4)
}

func TestRuntimeMultipleInterfaceDefaultImplementationsError(t *testing.T) {
t.Parallel()

runtime := newTestInterpreterRuntime()

makeDeployTransaction := func(name, code string) []byte {
return []byte(fmt.Sprintf(
`
transaction {
prepare(signer: AuthAccount) {
let acct = AuthAccount(payer: signer)
acct.contracts.add(name: "%s", code: "%s".decodeHex())
}
}
`,
name,
hex.EncodeToString([]byte(code)),
))
}

contractInterfaceCode := `
access(all) contract TestInterfaces {
access(all) resource interface A {
access(all) fun foo() {
let x = 3
}
}
access(all) resource interface B {
access(all) fun foo() {
let x = 4
}
}
}
`

contractCode := `
import TestInterfaces from 0x2
access(all) contract TestContract {
access(all) resource R: TestInterfaces.A, TestInterfaces.B {}
// fill space
// fill space
// fill space
// fill space
// fill space
// fill space
// filling lots of space
// filling lots of space
// filling lots of space
}
`

accountCodes := map[Location][]byte{}
var events []cadence.Event

var nextAccount byte = 0x2

runtimeInterface := &testRuntimeInterface{
getCode: func(location Location) (bytes []byte, err error) {
return accountCodes[location], nil
},
storage: newTestLedger(nil, nil),
createAccount: func(payer Address) (address Address, err error) {
result := interpreter.NewUnmeteredAddressValueFromBytes([]byte{nextAccount})
nextAccount++
return result.ToAddress(), nil
},
getSigningAccounts: func() ([]Address, error) {
return []Address{{0x1}}, nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
return accountCodes[location], nil
},
updateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
emitEvent: func(event cadence.Event) error {
events = append(events, event)
return nil
},
}

nextTransactionLocation := newTransactionLocationGenerator()

deployTransaction := makeDeployTransaction("TestInterfaces", contractInterfaceCode)
err := runtime.ExecuteTransaction(
Script{
Source: deployTransaction,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

deployTransaction = makeDeployTransaction("TestContract", contractCode)
err = runtime.ExecuteTransaction(
Script{
Source: deployTransaction,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
require.Error(t, err)
require.Contains(t, err.Error(), "access(all) resource R: TestInterfaces.A, TestInterfaces.B {}")

var errType *sema.CheckerError
require.ErrorAs(t, err, &errType)

checkerErr := err.(Error).
Err.(interpreter.Error).
Err.(*stdlib.InvalidContractDeploymentError).
Err.(*ParsingCheckingError).
Err.(*sema.CheckerError)

var specificErrType *sema.MultipleInterfaceDefaultImplementationsError
require.ErrorAs(t, checkerErr.Errors[0], &specificErrType)

errorRange := checkerErr.Errors[0].(*sema.MultipleInterfaceDefaultImplementationsError).Range

require.Equal(t, errorRange.StartPos.Line, 4)
}
7 changes: 7 additions & 0 deletions runtime/sema/check_composite_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,18 +738,22 @@ func (checker *Checker) declareCompositeLikeMembersAndValue(
}

if _, ok := inheritedMembers.Get(memberName); ok {
errorRange := ast.NewRangeFromPositioned(checker.memoryGauge, declaration.DeclarationIdentifier())

if member.HasImplementation {
checker.report(
&MultipleInterfaceDefaultImplementationsError{
CompositeType: nestedCompositeType,
Member: member,
Range: errorRange,
},
)
} else {
checker.report(
&DefaultFunctionConflictError{
CompositeType: nestedCompositeType,
Member: member,
Range: errorRange,
},
)
}
Expand Down Expand Up @@ -1243,18 +1247,21 @@ func (checker *Checker) checkCompositeLikeConformance(
if interfaceMember.DeclarationKind == common.DeclarationKindFunction {

if _, ok := inheritedMembers[name]; ok {
errorRange := ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.DeclarationIdentifier())
if interfaceMember.HasImplementation {
checker.report(
&MultipleInterfaceDefaultImplementationsError{
CompositeType: compositeType,
Member: interfaceMember,
Range: errorRange,
},
)
} else {
checker.report(
&DefaultFunctionConflictError{
CompositeType: compositeType,
Member: interfaceMember,
Range: errorRange,
},
)
}
Expand Down
20 changes: 3 additions & 17 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,7 @@ func (e *DuplicateConformanceError) Error() string {
type MultipleInterfaceDefaultImplementationsError struct {
CompositeType *CompositeType
Member *Member
ast.Range
}

var _ SemanticError = &MultipleInterfaceDefaultImplementationsError{}
Expand All @@ -1581,14 +1582,6 @@ func (e *MultipleInterfaceDefaultImplementationsError) Error() string {
)
}

func (e *MultipleInterfaceDefaultImplementationsError) StartPosition() ast.Position {
return e.Member.Identifier.StartPosition()
}

func (e *MultipleInterfaceDefaultImplementationsError) EndPosition(memoryGauge common.MemoryGauge) ast.Position {
return e.Member.Identifier.EndPosition(memoryGauge)
}

// SpecialFunctionDefaultImplementationError
type SpecialFunctionDefaultImplementationError struct {
Container ast.Declaration
Expand Down Expand Up @@ -1624,6 +1617,7 @@ func (e *SpecialFunctionDefaultImplementationError) EndPosition(memoryGauge comm
type DefaultFunctionConflictError struct {
CompositeType *CompositeType
Member *Member
ast.Range
}

var _ SemanticError = &DefaultFunctionConflictError{}
Expand All @@ -1635,21 +1629,13 @@ func (*DefaultFunctionConflictError) IsUserError() {}

func (e *DefaultFunctionConflictError) Error() string {
return fmt.Sprintf(
"%s `%s` has conflicting requirements for function `%s`",
"%s `%s` has conflicting requirements for function `%s` ",
e.CompositeType.Kind.Name(),
e.CompositeType.QualifiedString(),
e.Member.Identifier.Identifier,
)
}

func (e *DefaultFunctionConflictError) StartPosition() ast.Position {
return e.Member.Identifier.StartPosition()
}

func (e *DefaultFunctionConflictError) EndPosition(memoryGauge common.MemoryGauge) ast.Position {
return e.Member.Identifier.EndPosition(memoryGauge)
}

// MissingConformanceError
type MissingConformanceError struct {
CompositeType *CompositeType
Expand Down

0 comments on commit 181924e

Please sign in to comment.