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

Add native function declarations for all relevant Test contract functions #2926

Closed
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
4 changes: 2 additions & 2 deletions runtime/parser/declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ func parseEventDeclaration(

// if this is a `ResourceDestroyed` event (i.e., a default event declaration), parse default arguments
parseDefaultArguments := ast.IsResourceDestructionDefaultEvent(identifier.Identifier)
parameterList, err := parseParameterList(p, parseDefaultArguments)
parameterList, err := parseParameterList(p, parseDefaultArguments, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1889,7 +1889,7 @@ func parseSpecialFunctionDeclaration(
startPos := ast.EarliestPosition(identifier.Pos, accessPos, purityPos, staticPos, nativePos)

parameterList, returnTypeAnnotation, functionBlock, err :=
parseFunctionParameterListAndRest(p, functionBlockIsOptional)
parseFunctionParameterListAndRest(p, functionBlockIsOptional, nativePos != nil)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/parser/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func TestParseParameterList(t *testing.T) {
nil,
[]byte(input),
func(p *parser) (*ast.ParameterList, error) {
return parseParameterList(p, false)
return parseParameterList(p, false, false)
},
Config{},
)
Expand Down
2 changes: 1 addition & 1 deletion runtime/parser/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ func defineIdentifierExpression() {

func parseFunctionExpression(p *parser, token lexer.Token, purity ast.FunctionPurity) (*ast.FunctionExpression, error) {
parameterList, returnTypeAnnotation, functionBlock, err :=
parseFunctionParameterListAndRest(p, false)
parseFunctionParameterListAndRest(p, false, false)
if err != nil {
return nil, err
}
Expand Down
35 changes: 30 additions & 5 deletions runtime/parser/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ func parsePurityAnnotation(p *parser) ast.FunctionPurity {
return ast.FunctionPurityUnspecified
}

func parseParameterList(p *parser, expectDefaultArguments bool) (*ast.ParameterList, error) {
func parseParameterList(
p *parser,
expectDefaultArguments bool,
acceptDefaultArguments bool,
) (*ast.ParameterList, error) {
var parameters []*ast.Parameter

p.skipSpaceAndComments()
Expand Down Expand Up @@ -63,7 +67,11 @@ func parseParameterList(p *parser, expectDefaultArguments bool) (*ast.ParameterL
Pos: p.current.StartPos,
})
}
parameter, err := parseParameter(p, expectDefaultArguments)
parameter, err := parseParameter(
p,
expectDefaultArguments,
acceptDefaultArguments,
)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -120,7 +128,11 @@ func parseParameterList(p *parser, expectDefaultArguments bool) (*ast.ParameterL
), nil
}

func parseParameter(p *parser, expectDefaultArgument bool) (*ast.Parameter, error) {
func parseParameter(
p *parser,
expectDefaultArgument bool,
acceptDefaultArgument bool,
) (*ast.Parameter, error) {
p.skipSpaceAndComments()

startPos := p.current.StartPos
Expand Down Expand Up @@ -187,6 +199,14 @@ func parseParameter(p *parser, expectDefaultArgument bool) (*ast.Parameter, erro
return nil, err
}

} else if acceptDefaultArgument && p.current.Is(lexer.TokenEqual) {
// Skip the =
p.nextSemanticToken()

defaultArgument, err = parseExpression(p, lowestBindingPower)
if err != nil {
return nil, err
}
} else if p.current.Is(lexer.TokenEqual) {
return nil, p.syntaxError("cannot use a default argument for this function")
}
Expand Down Expand Up @@ -354,7 +374,11 @@ func parseFunctionDeclaration(
}

parameterList, returnTypeAnnotation, functionBlock, err :=
parseFunctionParameterListAndRest(p, functionBlockIsOptional)
parseFunctionParameterListAndRest(
p,
functionBlockIsOptional,
nativePos != nil,
)

if err != nil {
return nil, err
Expand All @@ -379,6 +403,7 @@ func parseFunctionDeclaration(
func parseFunctionParameterListAndRest(
p *parser,
functionBlockIsOptional bool,
isNative bool,
) (
parameterList *ast.ParameterList,
returnTypeAnnotation *ast.TypeAnnotation,
Expand All @@ -387,7 +412,7 @@ func parseFunctionParameterListAndRest(
) {
// Parameter list

parameterList, err = parseParameterList(p, false)
parameterList, err = parseParameterList(p, false, isNative)
if err != nil {
return
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/parser/statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func parseFunctionDeclarationOrFunctionExpressionStatement(
}

parameterList, returnTypeAnnotation, functionBlock, err :=
parseFunctionParameterListAndRest(p, false)
parseFunctionParameterListAndRest(p, false, false)

if err != nil {
return nil, err
Expand All @@ -227,7 +227,7 @@ func parseFunctionDeclarationOrFunctionExpressionStatement(
), nil
} else {
parameterList, returnTypeAnnotation, functionBlock, err :=
parseFunctionParameterListAndRest(p, false)
parseFunctionParameterListAndRest(p, false, false)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/parser/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func parseTransactionDeclaration(p *parser, docString string) (*ast.TransactionD
var err error

if p.current.Is(lexer.TokenParenOpen) {
parameterList, err = parseParameterList(p, false)
parameterList, err = parseParameterList(p, false, false)
if err != nil {
return nil, err
}
Expand Down
14 changes: 2 additions & 12 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -4738,11 +4738,7 @@ func (t *CompositeType) GetMembers() map[string]MemberResolver {
}

func (t *CompositeType) initializeMemberResolvers() {
t.memberResolversOnce.Do(t.initializerMemberResolversFunc())
}

func (t *CompositeType) initializerMemberResolversFunc() func() {
return func() {
t.memberResolversOnce.Do(func() {
memberResolvers := MembersMapAsResolvers(t.Members)

// Check conformances.
Expand Down Expand Up @@ -4777,13 +4773,7 @@ func (t *CompositeType) initializerMemberResolversFunc() func() {
}

t.memberResolvers = withBuiltinMembers(t, memberResolvers)
}
}

func (t *CompositeType) ResolveMembers() {
if t.Members.Len() != len(t.GetMembers()) {
t.initializerMemberResolversFunc()()
}
})
}

func (t *CompositeType) FieldPosition(name string, declaration ast.CompositeLikeDeclaration) ast.Position {
Expand Down
83 changes: 83 additions & 0 deletions runtime/stdlib/contracts/test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -473,4 +473,87 @@ contract Test {

assert(found, message: "the error message did not contain the given sub-string")
}

/// Creates a matcher with a test function.
/// The test function is of type 'fun(T): Bool',
/// where 'T' is bound to 'AnyStruct'.
///
access(all)
native fun newMatcher<T: AnyStruct>(_ test: fun(T): Bool): Test.Matcher {}

/// Wraps a function call in a closure, and expects it to fail with
/// an error message that contains the given error message portion.
///
access(all)
native fun expectFailure(
_ functionWrapper: fun(): Void,
errorMessageSubstring: String
) {}

/// Expect function tests a value against a matcher
/// and fails the test if it's not a match.
///
access(all)
native fun expect<T: AnyStruct>(_ value: T, _ matcher: Test.Matcher) {}

/// Returns a matcher that succeeds if the tested
/// value is equal to the given value.
///
access(all)
native fun equal<T: AnyStruct>(_ value: T): Test.Matcher {}

/// Fails the test-case if the given values are not equal, and
/// reports a message which explains how the two values differ.
///
access(all)
native fun assertEqual(_ expected: AnyStruct, _ actual: AnyStruct) {}

/// Returns a matcher that succeeds if the tested value is
/// an array or dictionary and the tested value contains
/// no elements.
///
access(all)
native fun beEmpty(): Test.Matcher {}

/// Returns a matcher that succeeds if the tested value is
/// an array or dictionary and has the given number of elements.
///
access(all)
native fun haveElementCount(_ count: Int): Test.Matcher {}

/// Returns a matcher that succeeds if the tested value is
/// an array that contains a value that is equal to the given
/// value, or the tested value is a dictionary that contains
/// an entry where the key is equal to the given value.
///
access(all)
native fun contain(_ element: AnyStruct): Test.Matcher {}

/// Returns a matcher that succeeds if the tested value
/// is a number and greater than the given number.
///
access(all)
native fun beGreaterThan(_ value: Number): Test.Matcher {}

/// Returns a matcher that succeeds if the tested value
/// is a number and less than the given number.
///
access(all)
native fun beLessThan(_ value: Number): Test.Matcher {}

/// Read a local file, and return the content as a string.
///
access(all)
native fun readFile(_ path: String): String {}

/// Fails the test-case if the given condition is false,
/// and reports a message which explains how the condition is false.
///
access(all)
native fun assert(_ condition: Bool, message: String = ""): Void {}

/// Fails the test-case with a message.
///
access(all)
native fun fail(message: String = ""): Void {}
}
23 changes: 12 additions & 11 deletions runtime/stdlib/test_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,12 @@ var testTypeAssertFunctionType = &sema.FunctionType{
TypeAnnotation: sema.BoolTypeAnnotation,
},
{
Identifier: "message",
TypeAnnotation: sema.StringTypeAnnotation,
Identifier: "message",
TypeAnnotation: sema.StringTypeAnnotation,
DefaultArgument: sema.StringType,
},
},
ReturnTypeAnnotation: sema.VoidTypeAnnotation,
// `message` parameter is optional
Arity: &sema.Arity{Min: 1, Max: 2},
}

var testTypeAssertFunction = interpreter.NewUnmeteredHostFunctionValue(
Expand Down Expand Up @@ -181,13 +180,12 @@ var testTypeFailFunctionType = &sema.FunctionType{
Purity: sema.FunctionPurityView,
Parameters: []sema.Parameter{
{
Identifier: "message",
TypeAnnotation: sema.StringTypeAnnotation,
Identifier: "message",
TypeAnnotation: sema.StringTypeAnnotation,
DefaultArgument: sema.StringType,
},
},
ReturnTypeAnnotation: sema.VoidTypeAnnotation,
// `message` parameter is optional
Arity: &sema.Arity{Min: 0, Max: 1},
}

var testTypeFailFunction = interpreter.NewUnmeteredHostFunctionValue(
Expand Down Expand Up @@ -915,7 +913,10 @@ func newTestContractType() *TestContractType {
program, err := parser.ParseProgram(
nil,
contracts.TestContract,
parser.Config{},
parser.Config{
NativeModifierEnabled: true,
TypeParametersEnabled: true,
},
)
if err != nil {
panic(err)
Expand All @@ -933,7 +934,8 @@ func newTestContractType() *TestContractType {
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
return activation
},
AccessCheckMode: sema.AccessCheckModeStrict,
AccessCheckMode: sema.AccessCheckModeStrict,
AllowNativeDeclarations: true,
},
)
if err != nil {
Expand Down Expand Up @@ -1160,7 +1162,6 @@ func newTestContractType() *TestContractType {
ty.expectFailureFunction = newTestTypeExpectFailureFunction(
expectFailureFunctionType,
)
compositeType.ResolveMembers()

return ty
}
Expand Down
Loading
Loading