From e94adb328e58c5473ae467fbc5edba6d7371c30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 25 Sep 2024 14:05:56 -0700 Subject: [PATCH 1/3] only declare result variable if post conditions are declared, provide better, dedicated error --- runtime/ast/block.go | 11 +- runtime/ast/block_test.go | 236 ++++---- runtime/ast/expression_test.go | 56 +- runtime/ast/transaction_declaration_test.go | 90 ++- runtime/coverage.go | 4 +- runtime/interpreter/interpreter.go | 30 +- runtime/interpreter/interpreter_expression.go | 6 +- .../interpreter/interpreter_transaction.go | 4 +- runtime/interpreter/value_function.go | 8 +- runtime/old_parser/declaration_test.go | 416 +++++++------ runtime/old_parser/statement.go | 14 +- runtime/old_parser/transaction.go | 8 +- runtime/parser/declaration_test.go | 564 ++++++++++-------- runtime/parser/statement.go | 28 +- runtime/parser/transaction.go | 10 +- runtime/runtime_test.go | 27 + runtime/sema/check_conditions.go | 4 +- runtime/sema/check_function.go | 152 +++-- runtime/sema/check_transaction_declaration.go | 3 +- runtime/sema/errors.go | 73 +++ runtime/sema/post_conditions_rewrite.go | 2 +- runtime/tests/checker/conditions_test.go | 8 +- runtime/tests/checker/function_test.go | 20 +- 23 files changed, 1047 insertions(+), 727 deletions(-) diff --git a/runtime/ast/block.go b/runtime/ast/block.go index 1f1b70a596..757233a843 100644 --- a/runtime/ast/block.go +++ b/runtime/ast/block.go @@ -355,10 +355,13 @@ func (c *EmitCondition) Walk(walkChild func(Element)) { // Conditions -type Conditions []Condition +type Conditions struct { + Conditions []Condition + Range +} func (c *Conditions) IsEmpty() bool { - return c == nil || len(*c) == 0 + return c == nil || len(c.Conditions) == 0 } func (c *Conditions) Doc(keywordDoc prettier.Doc) prettier.Doc { @@ -368,7 +371,7 @@ func (c *Conditions) Doc(keywordDoc prettier.Doc) prettier.Doc { var doc prettier.Concat - for _, condition := range *c { + for _, condition := range c.Conditions { doc = append( doc, prettier.HardLine{}, @@ -395,7 +398,7 @@ func (c *Conditions) Walk(walkChild func(Element)) { return } - for _, condition := range *c { + for _, condition := range c.Conditions { walkChild(condition) } } diff --git a/runtime/ast/block_test.go b/runtime/ast/block_test.go index c8be0ab493..396c654a0a 100644 --- a/runtime/ast/block_test.go +++ b/runtime/ast/block_test.go @@ -220,45 +220,57 @@ func TestFunctionBlock_MarshalJSON(t *testing.T) { }, }, PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - Range: Range{ - StartPos: Position{Offset: 7, Line: 8, Column: 9}, - EndPos: Position{Offset: 10, Line: 11, Column: 12}, + Range: Range{ + StartPos: Position{Offset: 44, Line: 45, Column: 46}, + EndPos: Position{Offset: 47, Line: 48, Column: 49}, + }, + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + Range: Range{ + StartPos: Position{Offset: 7, Line: 8, Column: 9}, + EndPos: Position{Offset: 10, Line: 11, Column: 12}, + }, }, - }, - Message: &StringExpression{ - Value: "Pre failed", - Range: Range{ - StartPos: Position{Offset: 13, Line: 14, Column: 15}, - EndPos: Position{Offset: 16, Line: 17, Column: 18}, + Message: &StringExpression{ + Value: "Pre failed", + Range: Range{ + StartPos: Position{Offset: 13, Line: 14, Column: 15}, + EndPos: Position{Offset: 16, Line: 17, Column: 18}, + }, }, }, - }, - &EmitCondition{ - InvocationExpression: &InvocationExpression{ - InvokedExpression: &IdentifierExpression{ - Identifier: Identifier{ - Identifier: "foobar", - Pos: Position{Offset: 31, Line: 32, Column: 33}, + &EmitCondition{ + InvocationExpression: &InvocationExpression{ + InvokedExpression: &IdentifierExpression{ + Identifier: Identifier{ + Identifier: "foobar", + Pos: Position{Offset: 31, Line: 32, Column: 33}, + }, }, + TypeArguments: []*TypeAnnotation{}, + Arguments: []*Argument{}, + ArgumentsStartPos: Position{Offset: 34, Line: 35, Column: 36}, + EndPos: Position{Offset: 37, Line: 38, Column: 39}, }, - TypeArguments: []*TypeAnnotation{}, - Arguments: []*Argument{}, - ArgumentsStartPos: Position{Offset: 34, Line: 35, Column: 36}, - EndPos: Position{Offset: 37, Line: 38, Column: 39}, + StartPos: Position{Offset: 40, Line: 41, Column: 42}, }, - StartPos: Position{Offset: 40, Line: 41, Column: 42}, }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - Range: Range{ - StartPos: Position{Offset: 19, Line: 20, Column: 21}, - EndPos: Position{Offset: 22, Line: 23, Column: 24}, + Range: Range{ + StartPos: Position{Offset: 50, Line: 51, Column: 52}, + EndPos: Position{Offset: 53, Line: 54, Column: 55}, + }, + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + Range: Range{ + StartPos: Position{Offset: 19, Line: 20, Column: 21}, + EndPos: Position{Offset: 22, Line: 23, Column: 24}, + }, }, }, }, @@ -279,62 +291,70 @@ func TestFunctionBlock_MarshalJSON(t *testing.T) { "StartPos": {"Offset": 1, "Line": 2, "Column": 3}, "EndPos": {"Offset": 4, "Line": 5, "Column": 6} }, - "PreConditions": [ - { - "Type": "TestCondition", - "Test": { - "Type": "BoolExpression", - "Value": false, + "PreConditions": { + "StartPos": {"Offset": 44, "Line": 45, "Column": 46}, + "EndPos": {"Offset": 47, "Line": 48, "Column": 49}, + "Conditions": [ + { + "Type": "TestCondition", + "Test": { + "Type": "BoolExpression", + "Value": false, + "StartPos": {"Offset": 7, "Line": 8, "Column": 9}, + "EndPos": {"Offset": 10, "Line": 11, "Column": 12} + }, + "Message": { + "Type": "StringExpression", + "Value": "Pre failed", + "StartPos": {"Offset": 13, "Line": 14, "Column": 15}, + "EndPos": {"Offset": 16, "Line": 17, "Column": 18} + }, "StartPos": {"Offset": 7, "Line": 8, "Column": 9}, - "EndPos": {"Offset": 10, "Line": 11, "Column": 12} - }, - "Message": { - "Type": "StringExpression", - "Value": "Pre failed", - "StartPos": {"Offset": 13, "Line": 14, "Column": 15}, "EndPos": {"Offset": 16, "Line": 17, "Column": 18} }, - "StartPos": {"Offset": 7, "Line": 8, "Column": 9}, - "EndPos": {"Offset": 16, "Line": 17, "Column": 18} - }, - { - "Type": "EmitCondition", - "InvocationExpression": { - "Type": "InvocationExpression", - "InvokedExpression": { - "Type": "IdentifierExpression", - "Identifier": { - "Identifier": "foobar", + { + "Type": "EmitCondition", + "InvocationExpression": { + "Type": "InvocationExpression", + "InvokedExpression": { + "Type": "IdentifierExpression", + "Identifier": { + "Identifier": "foobar", + "StartPos": {"Offset": 31, "Line": 32, "Column": 33}, + "EndPos": {"Offset": 36, "Line": 32, "Column": 38} + }, "StartPos": {"Offset": 31, "Line": 32, "Column": 33}, "EndPos": {"Offset": 36, "Line": 32, "Column": 38} - }, - "StartPos": {"Offset": 31, "Line": 32, "Column": 33}, - "EndPos": {"Offset": 36, "Line": 32, "Column": 38} + }, + "TypeArguments": [], + "Arguments": [], + "ArgumentsStartPos": {"Offset": 34, "Line": 35, "Column": 36}, + "StartPos": {"Offset": 31, "Line": 32, "Column": 33}, + "EndPos": {"Offset": 37, "Line": 38, "Column": 39} }, - "TypeArguments": [], - "Arguments": [], - "ArgumentsStartPos": {"Offset": 34, "Line": 35, "Column": 36}, - "StartPos": {"Offset": 31, "Line": 32, "Column": 33}, + "StartPos": {"Offset": 40, "Line": 41, "Column": 42}, "EndPos": {"Offset": 37, "Line": 38, "Column": 39} - }, - "StartPos": {"Offset": 40, "Line": 41, "Column": 42}, - "EndPos": {"Offset": 37, "Line": 38, "Column": 39} - } - ], - "PostConditions": [ - { - "Type": "TestCondition", - "Test": { - "Type": "BoolExpression", - "Value": true, + } + ] + }, + "PostConditions": { + "StartPos": {"Offset": 50, "Line": 51, "Column": 52}, + "EndPos": {"Offset": 53, "Line": 54, "Column": 55}, + "Conditions": [ + { + "Type": "TestCondition", + "Test": { + "Type": "BoolExpression", + "Value": true, + "StartPos": {"Offset": 19, "Line": 20, "Column": 21}, + "EndPos": {"Offset": 22, "Line": 23, "Column": 24} + }, + "Message": null, "StartPos": {"Offset": 19, "Line": 20, "Column": 21}, "EndPos": {"Offset": 22, "Line": 23, "Column": 24} - }, - "Message": null, - "StartPos": {"Offset": 19, "Line": 20, "Column": 21}, - "EndPos": {"Offset": 22, "Line": 23, "Column": 24} - } - ], + } + ] + }, "StartPos": {"Offset": 1, "Line": 2, "Column": 3}, "EndPos": {"Offset": 4, "Line": 5, "Column": 6} } @@ -407,28 +427,32 @@ func TestFunctionBlock_Doc(t *testing.T) { }, }, PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "Pre failed", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "Pre failed", + }, }, - }, - &EmitCondition{ - InvocationExpression: &InvocationExpression{ - InvokedExpression: &IdentifierExpression{ - Identifier: Identifier{ - Identifier: "Foo", + &EmitCondition{ + InvocationExpression: &InvocationExpression{ + InvokedExpression: &IdentifierExpression{ + Identifier: Identifier{ + Identifier: "Foo", + }, }, }, }, }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, }, }, }, @@ -561,22 +585,26 @@ func TestFunctionBlock_String(t *testing.T) { }, }, PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "Pre failed", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "Pre failed", + }, }, }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - }, - Message: &StringExpression{ - Value: "Post failed", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, + Message: &StringExpression{ + Value: "Post failed", + }, }, }, }, diff --git a/runtime/ast/expression_test.go b/runtime/ast/expression_test.go index c37dbfda38..2ac2346546 100644 --- a/runtime/ast/expression_test.go +++ b/runtime/ast/expression_test.go @@ -4623,22 +4623,26 @@ func TestFunctionExpression_Doc(t *testing.T) { }, FunctionBlock: &FunctionBlock{ PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - }, - Message: &StringExpression{ - Value: "pre", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, + Message: &StringExpression{ + Value: "pre", + }, }, }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "post", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "post", + }, }, }, }, @@ -4877,22 +4881,26 @@ func TestFunctionExpression_String(t *testing.T) { }, FunctionBlock: &FunctionBlock{ PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - }, - Message: &StringExpression{ - Value: "pre", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, + Message: &StringExpression{ + Value: "pre", + }, }, }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "post", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "post", + }, }, }, }, diff --git a/runtime/ast/transaction_declaration_test.go b/runtime/ast/transaction_declaration_test.go index 44f4afd39c..9713bad7fe 100644 --- a/runtime/ast/transaction_declaration_test.go +++ b/runtime/ast/transaction_declaration_test.go @@ -41,12 +41,22 @@ func TestTransactionDeclaration_MarshalJSON(t *testing.T) { EndPos: Position{Offset: 4, Line: 5, Column: 6}, }, }, - Fields: []*FieldDeclaration{}, - Prepare: nil, - PreConditions: &Conditions{}, - PostConditions: &Conditions{}, - DocString: "test", - Execute: nil, + Fields: []*FieldDeclaration{}, + Prepare: nil, + PreConditions: &Conditions{ + Range: Range{ + StartPos: Position{Offset: 13, Line: 14, Column: 15}, + EndPos: Position{Offset: 16, Line: 17, Column: 18}, + }, + }, + PostConditions: &Conditions{ + Range: Range{ + StartPos: Position{Offset: 19, Line: 20, Column: 21}, + EndPos: Position{Offset: 22, Line: 23, Column: 24}, + }, + }, + DocString: "test", + Execute: nil, Range: Range{ StartPos: Position{Offset: 7, Line: 8, Column: 9}, EndPos: Position{Offset: 10, Line: 11, Column: 12}, @@ -68,8 +78,16 @@ func TestTransactionDeclaration_MarshalJSON(t *testing.T) { }, "Fields": [], "Prepare": null, - "PreConditions": [], - "PostConditions": [], + "PreConditions": { + "Conditions": null, + "StartPos": {"Offset": 13, "Line": 14, "Column": 15}, + "EndPos": {"Offset": 16, "Line": 17, "Column": 18} + }, + "PostConditions": { + "Conditions": null, + "StartPos": {"Offset": 19, "Line": 20, "Column": 21}, + "EndPos": {"Offset": 22, "Line": 23, "Column": 24} + }, "Execute": null, "DocString": "test", "StartPos": {"Offset": 7, "Line": 8, "Column": 9}, @@ -146,12 +164,14 @@ func TestTransactionDeclaration_Doc(t *testing.T) { }, }, PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - }, - Message: &StringExpression{ - Value: "pre", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, + Message: &StringExpression{ + Value: "pre", + }, }, }, }, @@ -173,12 +193,14 @@ func TestTransactionDeclaration_Doc(t *testing.T) { }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "post", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "post", + }, }, }, }, @@ -418,12 +440,14 @@ func TestTransactionDeclaration_String(t *testing.T) { }, }, PreConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: true, - }, - Message: &StringExpression{ - Value: "pre", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: true, + }, + Message: &StringExpression{ + Value: "pre", + }, }, }, }, @@ -445,12 +469,14 @@ func TestTransactionDeclaration_String(t *testing.T) { }, }, PostConditions: &Conditions{ - &TestCondition{ - Test: &BoolExpression{ - Value: false, - }, - Message: &StringExpression{ - Value: "post", + Conditions: []Condition{ + &TestCondition{ + Test: &BoolExpression{ + Value: false, + }, + Message: &StringExpression{ + Value: "post", + }, }, }, }, diff --git a/runtime/coverage.go b/runtime/coverage.go index 6be14155e1..9349942546 100644 --- a/runtime/coverage.go +++ b/runtime/coverage.go @@ -216,12 +216,12 @@ func (r *CoverageReport) InspectProgram(location Location, program *ast.Program) // Track also pre/post conditions defined inside functions. if isFunctionBlock { if functionBlock.PreConditions != nil { - for _, condition := range *functionBlock.PreConditions { + for _, condition := range functionBlock.PreConditions.Conditions { recordLine(condition.CodeElement()) } } if functionBlock.PostConditions != nil { - for _, condition := range *functionBlock.PostConditions { + for _, condition := range functionBlock.PostConditions.Conditions { recordLine(condition.CodeElement()) } } diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 94a62d5793..27b0735ea0 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -740,13 +740,13 @@ func (interpreter *Interpreter) functionDeclarationValue( lexicalScope *VariableActivation, ) *InterpretedFunctionValue { - var preConditions ast.Conditions + var preConditions []ast.Condition if declaration.FunctionBlock.PreConditions != nil { - preConditions = *declaration.FunctionBlock.PreConditions + preConditions = declaration.FunctionBlock.PreConditions.Conditions } var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition if declaration.FunctionBlock.PostConditions != nil { postConditionsRewrite := @@ -778,9 +778,9 @@ func (interpreter *Interpreter) visitBlock(block *ast.Block) StatementResult { func (interpreter *Interpreter) visitFunctionBody( beforeStatements []ast.Statement, - preConditions ast.Conditions, + preConditions []ast.Condition, body func() StatementResult, - postConditions ast.Conditions, + postConditions []ast.Condition, returnType sema.Type, declarationLocationRange LocationRange, ) Value { @@ -875,7 +875,7 @@ func (interpreter *Interpreter) resultValue(returnValue Value, returnType sema.T ) } -func (interpreter *Interpreter) visitConditions(conditions ast.Conditions, kind ast.ConditionKind) { +func (interpreter *Interpreter) visitConditions(conditions []ast.Condition, kind ast.ConditionKind) { for _, condition := range conditions { interpreter.visitCondition(condition, kind) } @@ -1674,15 +1674,15 @@ func (interpreter *Interpreter) compositeInitializerFunction( parameterList := initializer.FunctionDeclaration.ParameterList - var preConditions ast.Conditions + var preConditions []ast.Condition if initializer.FunctionDeclaration.FunctionBlock.PreConditions != nil { - preConditions = *initializer.FunctionDeclaration.FunctionBlock.PreConditions + preConditions = initializer.FunctionDeclaration.FunctionBlock.PreConditions.Conditions } statements := initializer.FunctionDeclaration.FunctionBlock.Block.Statements var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition postConditions := initializer.FunctionDeclaration.FunctionBlock.PostConditions if postConditions != nil { @@ -1791,14 +1791,14 @@ func (interpreter *Interpreter) compositeFunction( functionType := interpreter.Program.Elaboration.FunctionDeclarationFunctionType(functionDeclaration) - var preConditions ast.Conditions + var preConditions []ast.Condition if functionDeclaration.FunctionBlock.PreConditions != nil { - preConditions = *functionDeclaration.FunctionBlock.PreConditions + preConditions = functionDeclaration.FunctionBlock.PreConditions.Conditions } var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition if functionDeclaration.FunctionBlock.PostConditions != nil { @@ -2456,13 +2456,13 @@ func (interpreter *Interpreter) functionConditionsWrapper( return nil } - var preConditions ast.Conditions + var preConditions []ast.Condition if declaration.FunctionBlock.PreConditions != nil { - preConditions = *declaration.FunctionBlock.PreConditions + preConditions = declaration.FunctionBlock.PreConditions.Conditions } var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition if declaration.FunctionBlock.PostConditions != nil { diff --git a/runtime/interpreter/interpreter_expression.go b/runtime/interpreter/interpreter_expression.go index 71fa7fd8a7..e7b315f387 100644 --- a/runtime/interpreter/interpreter_expression.go +++ b/runtime/interpreter/interpreter_expression.go @@ -1297,13 +1297,13 @@ func (interpreter *Interpreter) VisitFunctionExpression(expression *ast.Function functionType := interpreter.Program.Elaboration.FunctionExpressionFunctionType(expression) - var preConditions ast.Conditions + var preConditions []ast.Condition if expression.FunctionBlock.PreConditions != nil { - preConditions = *expression.FunctionBlock.PreConditions + preConditions = expression.FunctionBlock.PreConditions.Conditions } var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition if expression.FunctionBlock.PostConditions != nil { postConditionsRewrite := diff --git a/runtime/interpreter/interpreter_transaction.go b/runtime/interpreter/interpreter_transaction.go index 6a5f0f932d..bf594e791e 100644 --- a/runtime/interpreter/interpreter_transaction.go +++ b/runtime/interpreter/interpreter_transaction.go @@ -129,9 +129,9 @@ func (interpreter *Interpreter) declareTransactionEntryPoint(declaration *ast.Tr } } - var preConditions ast.Conditions + var preConditions []ast.Condition if declaration.PreConditions != nil { - preConditions = *declaration.PreConditions + preConditions = declaration.PreConditions.Conditions } declarationLocationRange := LocationRange{ diff --git a/runtime/interpreter/value_function.go b/runtime/interpreter/value_function.go index cabccd80b5..18f061f67b 100644 --- a/runtime/interpreter/value_function.go +++ b/runtime/interpreter/value_function.go @@ -45,9 +45,9 @@ type InterpretedFunctionValue struct { Type *sema.FunctionType Activation *VariableActivation BeforeStatements []ast.Statement - PreConditions ast.Conditions + PreConditions []ast.Condition Statements []ast.Statement - PostConditions ast.Conditions + PostConditions []ast.Condition } func NewInterpretedFunctionValue( @@ -56,9 +56,9 @@ func NewInterpretedFunctionValue( functionType *sema.FunctionType, lexicalScope *VariableActivation, beforeStatements []ast.Statement, - preConditions ast.Conditions, + preConditions []ast.Condition, statements []ast.Statement, - postConditions ast.Conditions, + postConditions []ast.Condition, ) *InterpretedFunctionValue { common.UseMemory(interpreter, common.InterpretedFunctionValueMemoryUsage) diff --git a/runtime/old_parser/declaration_test.go b/runtime/old_parser/declaration_test.go index 1ecbc1e9bb..ba2b374f95 100644 --- a/runtime/old_parser/declaration_test.go +++ b/runtime/old_parser/declaration_test.go @@ -699,63 +699,67 @@ func TestParseFunctionDeclaration(t *testing.T) { }, FunctionBlock: &ast.FunctionBlock{ PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: true, - Range: ast.Range{ - StartPos: ast.Position{Line: 4, Column: 17, Offset: 61}, - EndPos: ast.Position{Line: 4, Column: 20, Offset: 64}, - }, - }, - Message: &ast.StringExpression{ - Value: "test", - Range: ast.Range{ - StartPos: ast.Position{Line: 4, Column: 24, Offset: 68}, - EndPos: ast.Position{Line: 4, Column: 29, Offset: 73}, - }, - }, - }, - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: true, Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 17, Offset: 92}, - EndPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + StartPos: ast.Position{Line: 4, Column: 17, Offset: 61}, + EndPos: ast.Position{Line: 4, Column: 20, Offset: 64}, }, }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("1"), - Value: big.NewInt(1), - Base: 10, + Message: &ast.StringExpression{ + Value: "test", Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 21, Offset: 96}, - EndPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + StartPos: ast.Position{Line: 4, Column: 24, Offset: 68}, + EndPos: ast.Position{Line: 4, Column: 29, Offset: 73}, }, }, }, - Message: &ast.StringExpression{ - Value: "foo", - Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 25, Offset: 100}, - EndPos: ast.Position{Line: 5, Column: 29, Offset: 104}, + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + EndPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + }, + }, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("1"), + Value: big.NewInt(1), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + EndPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + }, + }, + }, + Message: &ast.StringExpression{ + Value: "foo", + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 25, Offset: 100}, + EndPos: ast.Position{Line: 5, Column: 29, Offset: 104}, + }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: false, - Range: ast.Range{ - StartPos: ast.Position{Line: 9, Column: 17, Offset: 161}, - EndPos: ast.Position{Line: 9, Column: 21, Offset: 165}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: false, + Range: ast.Range{ + StartPos: ast.Position{Line: 9, Column: 17, Offset: 161}, + EndPos: ast.Position{Line: 9, Column: 21, Offset: 165}, + }, }, + Message: nil, }, - Message: nil, }, }, Block: &ast.Block{ @@ -3883,44 +3887,48 @@ func TestParseTransactionDeclaration(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, - EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 197, Line: 19, Column: 11}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 197, Line: 19, Column: 11}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 202, Line: 19, Column: 16}, - EndPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + EndPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + }, }, }, }, @@ -4118,44 +4126,48 @@ func TestParseTransactionDeclaration(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, - EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 154, Line: 15, Column: 11}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 154, Line: 15, Column: 11}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 159, Line: 15, Column: 16}, - EndPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + EndPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + }, }, }, }, @@ -4721,64 +4733,69 @@ func TestParsePreAndPostConditions(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationNotEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationNotEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, - EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + }, }, }, }, - }, - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 85, Line: 5, Column: 16}, + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 85, Line: 5, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 89, Line: 5, Column: 20}, - EndPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + EndPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "result", - Pos: ast.Position{Offset: 140, Line: 8, Column: 16}, + Conditions: []ast.Condition{ + + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "result", + Pos: ast.Position{Offset: 140, Line: 8, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 150, Line: 8, Column: 26}, - EndPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + EndPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + }, }, }, }, @@ -4862,32 +4879,34 @@ func TestParseConditionMessage(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreaterEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreaterEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + }, + }, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + }, }, }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, + Message: &ast.StringExpression{ + Value: "n must be positive", Range: ast.Range{ - StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, - EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + StartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, + EndPos: ast.Position{Offset: 89, Line: 4, Column: 43}, }, }, }, - Message: &ast.StringExpression{ - Value: "n must be positive", - Range: ast.Range{ - StartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, - EndPos: ast.Position{Offset: 89, Line: 4, Column: 43}, - }, - }, }, }, PostConditions: nil, @@ -6139,39 +6158,42 @@ func TestParsePreconditionWithUnaryNegation(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: true, - Range: ast.Range{ - StartPos: ast.Position{Offset: 47, Line: 4, Column: 14}, - EndPos: ast.Position{Offset: 50, Line: 4, Column: 17}, - }, - }, - Message: &ast.StringExpression{ - Value: "one", - Range: ast.Range{ - StartPos: ast.Position{Offset: 53, Line: 4, Column: 20}, - EndPos: ast.Position{Offset: 57, Line: 4, Column: 24}, + Conditions: []ast.Condition{ + + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: true, + Range: ast.Range{ + StartPos: ast.Position{Offset: 47, Line: 4, Column: 14}, + EndPos: ast.Position{Offset: 50, Line: 4, Column: 17}, + }, }, - }, - }, - &ast.TestCondition{ - Test: &ast.UnaryExpression{ - Operation: ast.OperationNegate, - Expression: &ast.BoolExpression{ - Value: false, + Message: &ast.StringExpression{ + Value: "one", Range: ast.Range{ - StartPos: ast.Position{Offset: 74, Line: 5, Column: 15}, - EndPos: ast.Position{Offset: 78, Line: 5, Column: 19}, + StartPos: ast.Position{Offset: 53, Line: 4, Column: 20}, + EndPos: ast.Position{Offset: 57, Line: 4, Column: 24}, }, }, - StartPos: ast.Position{Offset: 73, Line: 5, Column: 14}, }, - Message: &ast.StringExpression{ - Value: "two", - Range: ast.Range{ - StartPos: ast.Position{Offset: 81, Line: 5, Column: 22}, - EndPos: ast.Position{Offset: 85, Line: 5, Column: 26}, + &ast.TestCondition{ + Test: &ast.UnaryExpression{ + Operation: ast.OperationNegate, + Expression: &ast.BoolExpression{ + Value: false, + Range: ast.Range{ + StartPos: ast.Position{Offset: 74, Line: 5, Column: 15}, + EndPos: ast.Position{Offset: 78, Line: 5, Column: 19}, + }, + }, + StartPos: ast.Position{Offset: 73, Line: 5, Column: 14}, + }, + Message: &ast.StringExpression{ + Value: "two", + Range: ast.Range{ + StartPos: ast.Position{Offset: 81, Line: 5, Column: 22}, + EndPos: ast.Position{Offset: 85, Line: 5, Column: 26}, + }, }, }, }, diff --git a/runtime/old_parser/statement.go b/runtime/old_parser/statement.go index dfa73bb617..80a0ef5d37 100644 --- a/runtime/old_parser/statement.go +++ b/runtime/old_parser/statement.go @@ -492,12 +492,10 @@ func parseFunctionBlock(p *parser) (*ast.FunctionBlock, error) { var preConditions *ast.Conditions if p.isToken(p.current, lexer.TokenIdentifier, keywordPre) { p.next() - conditions, err := parseConditions(p, ast.ConditionKindPre) + preConditions, err = parseConditions(p, ast.ConditionKindPre) if err != nil { return nil, err } - - preConditions = &conditions } p.skipSpaceAndComments() @@ -505,12 +503,10 @@ func parseFunctionBlock(p *parser) (*ast.FunctionBlock, error) { var postConditions *ast.Conditions if p.isToken(p.current, lexer.TokenIdentifier, keywordPost) { p.next() - conditions, err := parseConditions(p, ast.ConditionKindPost) + postConditions, err = parseConditions(p, ast.ConditionKindPost) if err != nil { return nil, err } - - postConditions = &conditions } statements, err := parseStatements(p, func(token lexer.Token) bool { @@ -542,7 +538,7 @@ func parseFunctionBlock(p *parser) (*ast.FunctionBlock, error) { } // parseConditions parses conditions (pre/post) -func parseConditions(p *parser, kind ast.ConditionKind) (conditions ast.Conditions, err error) { +func parseConditions(p *parser, kind ast.ConditionKind) (conditions *ast.Conditions, err error) { p.skipSpaceAndComments() _, err = p.mustOne(lexer.TokenBraceOpen) @@ -550,6 +546,8 @@ func parseConditions(p *parser, kind ast.ConditionKind) (conditions ast.Conditio return nil, err } + conditions = &ast.Conditions{} + defer func() { p.skipSpaceAndComments() _, err = p.mustOne(lexer.TokenBraceClose) @@ -572,7 +570,7 @@ func parseConditions(p *parser, kind ast.ConditionKind) (conditions ast.Conditio return } - conditions = append(conditions, condition) + conditions.Conditions = append(conditions.Conditions, condition) } } } diff --git a/runtime/old_parser/transaction.go b/runtime/old_parser/transaction.go index a6c72419aa..a4e460f945 100644 --- a/runtime/old_parser/transaction.go +++ b/runtime/old_parser/transaction.go @@ -125,12 +125,10 @@ func parseTransactionDeclaration(p *parser, docString string) (*ast.TransactionD if p.isToken(p.current, lexer.TokenIdentifier, keywordPre) { // Skip the `pre` keyword p.next() - conditions, err := parseConditions(p, ast.ConditionKindPre) + preConditions, err = parseConditions(p, ast.ConditionKindPre) if err != nil { return nil, err } - - preConditions = &conditions } } @@ -166,12 +164,10 @@ func parseTransactionDeclaration(p *parser, docString string) (*ast.TransactionD } // Skip the `post` keyword p.next() - conditions, err := parseConditions(p, ast.ConditionKindPost) + postConditions, err = parseConditions(p, ast.ConditionKindPost) if err != nil { return nil, err } - - postConditions = &conditions sawPost = true default: diff --git a/runtime/parser/declaration_test.go b/runtime/parser/declaration_test.go index b2c47abda1..46af2369a4 100644 --- a/runtime/parser/declaration_test.go +++ b/runtime/parser/declaration_test.go @@ -764,63 +764,75 @@ func TestParseFunctionDeclaration(t *testing.T) { }, FunctionBlock: &ast.FunctionBlock{ PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: true, - Range: ast.Range{ - StartPos: ast.Position{Line: 4, Column: 17, Offset: 61}, - EndPos: ast.Position{Line: 4, Column: 20, Offset: 64}, - }, - }, - Message: &ast.StringExpression{ - Value: "test", - Range: ast.Range{ - StartPos: ast.Position{Line: 4, Column: 24, Offset: 68}, - EndPos: ast.Position{Line: 4, Column: 29, Offset: 73}, - }, - }, + Range: ast.Range{ + StartPos: ast.Position{Offset: 38, Line: 3, Column: 14}, + EndPos: ast.Position{Offset: 120, Line: 6, Column: 14}, }, - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: true, Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 17, Offset: 92}, - EndPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + StartPos: ast.Position{Line: 4, Column: 17, Offset: 61}, + EndPos: ast.Position{Line: 4, Column: 20, Offset: 64}, }, }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("1"), - Value: big.NewInt(1), - Base: 10, + Message: &ast.StringExpression{ + Value: "test", Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 21, Offset: 96}, - EndPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + StartPos: ast.Position{Line: 4, Column: 24, Offset: 68}, + EndPos: ast.Position{Line: 4, Column: 29, Offset: 73}, }, }, }, - Message: &ast.StringExpression{ - Value: "foo", - Range: ast.Range{ - StartPos: ast.Position{Line: 5, Column: 25, Offset: 100}, - EndPos: ast.Position{Line: 5, Column: 29, Offset: 104}, + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + EndPos: ast.Position{Line: 5, Column: 17, Offset: 92}, + }, + }, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("1"), + Value: big.NewInt(1), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + EndPos: ast.Position{Line: 5, Column: 21, Offset: 96}, + }, + }, + }, + Message: &ast.StringExpression{ + Value: "foo", + Range: ast.Range{ + StartPos: ast.Position{Line: 5, Column: 25, Offset: 100}, + EndPos: ast.Position{Line: 5, Column: 29, Offset: 104}, + }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: false, - Range: ast.Range{ - StartPos: ast.Position{Line: 9, Column: 17, Offset: 161}, - EndPos: ast.Position{Line: 9, Column: 21, Offset: 165}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 137, Line: 8, Column: 14}, + EndPos: ast.Position{Offset: 181, Line: 10, Column: 14}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: false, + Range: ast.Range{ + StartPos: ast.Position{Line: 9, Column: 17, Offset: 161}, + EndPos: ast.Position{Line: 9, Column: 21, Offset: 165}, + }, }, + Message: nil, }, - Message: nil, }, }, Block: &ast.Block{ @@ -5228,44 +5240,56 @@ func TestParseTransactionDeclaration(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 101, Line: 10, Column: 3}, + EndPos: ast.Position{Offset: 127, Line: 12, Column: 3}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, - EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 197, Line: 19, Column: 11}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 179, Line: 18, Column: 6}, + EndPos: ast.Position{Offset: 213, Line: 20, Column: 9}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 197, Line: 19, Column: 11}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 202, Line: 19, Column: 16}, - EndPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + EndPos: ast.Position{Offset: 202, Line: 19, Column: 16}, + }, }, }, }, @@ -5463,44 +5487,56 @@ func TestParseTransactionDeclaration(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 101, Line: 10, Column: 3}, + EndPos: ast.Position{Offset: 127, Line: 12, Column: 3}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 117, Line: 11, Column: 10}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, - EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + EndPos: ast.Position{Offset: 122, Line: 11, Column: 15}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "x", - Pos: ast.Position{Offset: 154, Line: 15, Column: 11}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 136, Line: 14, Column: 6}, + EndPos: ast.Position{Offset: 170, Line: 16, Column: 9}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Offset: 154, Line: 15, Column: 11}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("2"), - Value: big.NewInt(2), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 159, Line: 15, Column: 16}, - EndPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("2"), + Value: big.NewInt(2), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + EndPos: ast.Position{Offset: 159, Line: 15, Column: 16}, + }, }, }, }, @@ -6259,64 +6295,76 @@ func TestParsePreAndPostConditions(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationNotEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 40, Line: 3, Column: 12}, + EndPos: ast.Position{Offset: 103, Line: 6, Column: 12}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationNotEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, - EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + }, }, }, }, - }, - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 85, Line: 5, Column: 16}, + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 85, Line: 5, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 89, Line: 5, Column: 20}, - EndPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + EndPos: ast.Position{Offset: 89, Line: 5, Column: 20}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "result", - Pos: ast.Position{Offset: 140, Line: 8, Column: 16}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 117, Line: 7, Column: 12}, + EndPos: ast.Position{Offset: 164, Line: 9, Column: 12}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "result", + Pos: ast.Position{Offset: 140, Line: 8, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 150, Line: 8, Column: 26}, - EndPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + EndPos: ast.Position{Offset: 150, Line: 8, Column: 26}, + }, }, }, }, @@ -6400,32 +6448,38 @@ func TestParseConditionMessage(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreaterEqual, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 40, Line: 3, Column: 12}, + EndPos: ast.Position{Offset: 103, Line: 5, Column: 12}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreaterEqual, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 62, Line: 4, Column: 16}, + }, + }, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + }, }, }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, + Message: &ast.StringExpression{ + Value: "n must be positive", Range: ast.Range{ - StartPos: ast.Position{Offset: 67, Line: 4, Column: 21}, - EndPos: ast.Position{Offset: 67, Line: 4, Column: 21}, + StartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, + EndPos: ast.Position{Offset: 89, Line: 4, Column: 43}, }, }, }, - Message: &ast.StringExpression{ - Value: "n must be positive", - Range: ast.Range{ - StartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, - EndPos: ast.Position{Offset: 89, Line: 4, Column: 43}, - }, - }, }, }, PostConditions: nil, @@ -6563,73 +6617,85 @@ func TestParseEmitAndTestCondition(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.EmitCondition{ - InvocationExpression: &ast.InvocationExpression{ - InvokedExpression: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "Foo", - Pos: ast.Position{Offset: 67, Line: 4, Column: 21}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 40, Line: 3, Column: 12}, + EndPos: ast.Position{Offset: 107, Line: 6, Column: 12}, + }, + Conditions: []ast.Condition{ + &ast.EmitCondition{ + InvocationExpression: &ast.InvocationExpression{ + InvokedExpression: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "Foo", + Pos: ast.Position{Offset: 67, Line: 4, Column: 21}, + }, }, + ArgumentsStartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, + EndPos: ast.Position{Offset: 71, Line: 4, Column: 25}, }, - ArgumentsStartPos: ast.Position{Offset: 70, Line: 4, Column: 24}, - EndPos: ast.Position{Offset: 71, Line: 4, Column: 25}, + StartPos: ast.Position{Offset: 62, Line: 4, Column: 16}, }, - StartPos: ast.Position{Offset: 62, Line: 4, Column: 16}, - }, - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 89, Line: 5, Column: 16}, + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 89, Line: 5, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 93, Line: 5, Column: 20}, - EndPos: ast.Position{Offset: 93, Line: 5, Column: 20}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 93, Line: 5, Column: 20}, + EndPos: ast.Position{Offset: 93, Line: 5, Column: 20}, + }, }, }, }, }, }, PostConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BinaryExpression{ - Operation: ast.OperationGreater, - Left: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "n", - Pos: ast.Position{Offset: 144, Line: 8, Column: 16}, + Range: ast.Range{ + StartPos: ast.Position{Offset: 121, Line: 7, Column: 12}, + EndPos: ast.Position{Offset: 189, Line: 10, Column: 12}, + }, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BinaryExpression{ + Operation: ast.OperationGreater, + Left: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "n", + Pos: ast.Position{Offset: 144, Line: 8, Column: 16}, + }, }, - }, - Right: &ast.IntegerExpression{ - PositiveLiteral: []byte("0"), - Value: new(big.Int), - Base: 10, - Range: ast.Range{ - StartPos: ast.Position{Offset: 148, Line: 8, Column: 20}, - EndPos: ast.Position{Offset: 148, Line: 8, Column: 20}, + Right: &ast.IntegerExpression{ + PositiveLiteral: []byte("0"), + Value: new(big.Int), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Offset: 148, Line: 8, Column: 20}, + EndPos: ast.Position{Offset: 148, Line: 8, Column: 20}, + }, }, }, }, - }, - &ast.EmitCondition{ - InvocationExpression: &ast.InvocationExpression{ - InvokedExpression: &ast.IdentifierExpression{ - Identifier: ast.Identifier{ - Identifier: "Bar", - Pos: ast.Position{Offset: 171, Line: 9, Column: 21}, + &ast.EmitCondition{ + InvocationExpression: &ast.InvocationExpression{ + InvokedExpression: &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "Bar", + Pos: ast.Position{Offset: 171, Line: 9, Column: 21}, + }, }, + ArgumentsStartPos: ast.Position{Offset: 174, Line: 9, Column: 24}, + EndPos: ast.Position{Offset: 175, Line: 9, Column: 25}, }, - ArgumentsStartPos: ast.Position{Offset: 174, Line: 9, Column: 24}, - EndPos: ast.Position{Offset: 175, Line: 9, Column: 25}, + StartPos: ast.Position{Offset: 166, Line: 9, Column: 16}, }, - StartPos: ast.Position{Offset: 166, Line: 9, Column: 16}, }, }, }, @@ -8031,39 +8097,45 @@ func TestParsePreconditionWithUnaryNegation(t *testing.T) { }, }, PreConditions: &ast.Conditions{ - &ast.TestCondition{ - Test: &ast.BoolExpression{ - Value: true, - Range: ast.Range{ - StartPos: ast.Position{Offset: 47, Line: 4, Column: 14}, - EndPos: ast.Position{Offset: 50, Line: 4, Column: 17}, - }, - }, - Message: &ast.StringExpression{ - Value: "one", - Range: ast.Range{ - StartPos: ast.Position{Offset: 53, Line: 4, Column: 20}, - EndPos: ast.Position{Offset: 57, Line: 4, Column: 24}, - }, - }, + Range: ast.Range{ + StartPos: ast.Position{Offset: 27, Line: 3, Column: 10}, + EndPos: ast.Position{Offset: 97, Line: 6, Column: 10}, }, - &ast.TestCondition{ - Test: &ast.UnaryExpression{ - Operation: ast.OperationNegate, - Expression: &ast.BoolExpression{ - Value: false, + Conditions: []ast.Condition{ + &ast.TestCondition{ + Test: &ast.BoolExpression{ + Value: true, + Range: ast.Range{ + StartPos: ast.Position{Offset: 47, Line: 4, Column: 14}, + EndPos: ast.Position{Offset: 50, Line: 4, Column: 17}, + }, + }, + Message: &ast.StringExpression{ + Value: "one", Range: ast.Range{ - StartPos: ast.Position{Offset: 74, Line: 5, Column: 15}, - EndPos: ast.Position{Offset: 78, Line: 5, Column: 19}, + StartPos: ast.Position{Offset: 53, Line: 4, Column: 20}, + EndPos: ast.Position{Offset: 57, Line: 4, Column: 24}, }, }, - StartPos: ast.Position{Offset: 73, Line: 5, Column: 14}, }, - Message: &ast.StringExpression{ - Value: "two", - Range: ast.Range{ - StartPos: ast.Position{Offset: 81, Line: 5, Column: 22}, - EndPos: ast.Position{Offset: 85, Line: 5, Column: 26}, + &ast.TestCondition{ + Test: &ast.UnaryExpression{ + Operation: ast.OperationNegate, + Expression: &ast.BoolExpression{ + Value: false, + Range: ast.Range{ + StartPos: ast.Position{Offset: 74, Line: 5, Column: 15}, + EndPos: ast.Position{Offset: 78, Line: 5, Column: 19}, + }, + }, + StartPos: ast.Position{Offset: 73, Line: 5, Column: 14}, + }, + Message: &ast.StringExpression{ + Value: "two", + Range: ast.Range{ + StartPos: ast.Position{Offset: 81, Line: 5, Column: 22}, + EndPos: ast.Position{Offset: 85, Line: 5, Column: 26}, + }, }, }, }, diff --git a/runtime/parser/statement.go b/runtime/parser/statement.go index cd5c9b93e4..3f84bfccc8 100644 --- a/runtime/parser/statement.go +++ b/runtime/parser/statement.go @@ -520,26 +520,26 @@ func parseFunctionBlock(p *parser) (*ast.FunctionBlock, error) { var preConditions *ast.Conditions if p.isToken(p.current, lexer.TokenIdentifier, KeywordPre) { + prePos := p.current.StartPos + // Skip the `pre` keyword p.next() - conditions, err := parseConditions(p) + preConditions, err = parseConditions(p, prePos) if err != nil { return nil, err } - - preConditions = &conditions } p.skipSpaceAndComments() var postConditions *ast.Conditions if p.isToken(p.current, lexer.TokenIdentifier, KeywordPost) { + startPos := p.current.StartPos + // Skip the `post` keyword p.next() - conditions, err := parseConditions(p) + postConditions, err = parseConditions(p, startPos) if err != nil { return nil, err } - - postConditions = &conditions } statements, err := parseStatements(p, func(token lexer.Token) bool { @@ -571,14 +571,16 @@ func parseFunctionBlock(p *parser) (*ast.FunctionBlock, error) { } // parseConditions parses conditions (pre/post) -func parseConditions(p *parser) (conditions ast.Conditions, err error) { +func parseConditions(p *parser, startPos ast.Position) (*ast.Conditions, error) { p.skipSpaceAndComments() - _, err = p.mustOne(lexer.TokenBraceOpen) + _, err := p.mustOne(lexer.TokenBraceOpen) if err != nil { return nil, err } + var conditions []ast.Condition + var done bool for !done { p.skipSpaceAndComments() @@ -594,7 +596,7 @@ func parseConditions(p *parser) (conditions ast.Conditions, err error) { var condition ast.Condition condition, err = parseCondition(p) if err != nil || condition == nil { - return + return nil, err } conditions = append(conditions, condition) @@ -602,12 +604,18 @@ func parseConditions(p *parser) (conditions ast.Conditions, err error) { } p.skipSpaceAndComments() + + endPos := p.current.StartPos + _, err = p.mustOne(lexer.TokenBraceClose) if err != nil { return nil, err } - return conditions, nil + return &ast.Conditions{ + Conditions: conditions, + Range: ast.NewRange(p.memoryGauge, startPos, endPos), + }, nil } // parseCondition parses a condition (pre/post) diff --git a/runtime/parser/transaction.go b/runtime/parser/transaction.go index 22b2b46184..ba31b1060d 100644 --- a/runtime/parser/transaction.go +++ b/runtime/parser/transaction.go @@ -125,14 +125,13 @@ func parseTransactionDeclaration(p *parser, docString string) (*ast.TransactionD if execute == nil { p.skipSpaceAndComments() if p.isToken(p.current, lexer.TokenIdentifier, KeywordPre) { + preStartPos := p.current.StartPos // Skip the `pre` keyword p.next() - conditions, err := parseConditions(p) + preConditions, err = parseConditions(p, preStartPos) if err != nil { return nil, err } - - preConditions = &conditions } } @@ -166,14 +165,13 @@ func parseTransactionDeclaration(p *parser, docString string) (*ast.TransactionD if sawPost { return nil, p.syntaxError("unexpected second post-conditions") } + postStartPos := p.current.StartPos // Skip the `post` keyword p.next() - conditions, err := parseConditions(p) + postConditions, err = parseConditions(p, postStartPos) if err != nil { return nil, err } - - postConditions = &conditions sawPost = true default: diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index cabe70f306..b6d6cdf629 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -11480,3 +11480,30 @@ func TestRuntimeStorageEnumAsDictionaryKey(t *testing.T) { loggedMessages, ) } + +func TestResultRedeclared(t *testing.T) { + + t.Parallel() + + runtime := NewTestInterpreterRuntime() + + script := []byte(` + access(all) fun main(): Int { let result = 1; return result } + `) + + runtimeInterface := &TestRuntimeInterface{} + + nextScriptLocation := NewScriptLocationGenerator() + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + +} diff --git a/runtime/sema/check_conditions.go b/runtime/sema/check_conditions.go index c404a6585f..e9ca18163b 100644 --- a/runtime/sema/check_conditions.go +++ b/runtime/sema/check_conditions.go @@ -66,11 +66,11 @@ func (checker *Checker) checkCondition(condition ast.Condition) { } } -func (checker *Checker) rewritePostConditions(postConditions ast.Conditions) PostConditionsRewrite { +func (checker *Checker) rewritePostConditions(postConditions []ast.Condition) PostConditionsRewrite { var beforeStatements []ast.Statement - var rewrittenPostConditions ast.Conditions + var rewrittenPostConditions []ast.Condition var allExtractedExpressions []ast.ExtractedExpression count := len(postConditions) diff --git a/runtime/sema/check_function.go b/runtime/sema/check_function.go index 4a716a204a..dc24c119da 100644 --- a/runtime/sema/check_function.go +++ b/runtime/sema/check_function.go @@ -222,7 +222,8 @@ func (checker *Checker) checkFunction( checker.InNewPurityScope(functionType.Purity == FunctionPurityView, func() { checker.visitFunctionBlock( functionBlock, - functionType.ReturnTypeAnnotation, + functionType.ReturnTypeAnnotation.Type, + returnTypeAnnotation, checkResourceLoss, ) }) @@ -357,8 +358,14 @@ func (checker *Checker) declareParameters( } } -func (checker *Checker) visitWithPostConditions(postConditions *ast.Conditions, returnType Type, body func()) { +func (checker *Checker) visitWithPostConditions( + postConditions *ast.Conditions, + returnType Type, + returnTypePos ast.HasPosition, + body func(), +) { + var postConditionsPos ast.Position var rewrittenPostConditions *PostConditionsRewrite // If there are post-conditions, rewrite them, extracting `before` expressions. @@ -366,7 +373,9 @@ func (checker *Checker) visitWithPostConditions(postConditions *ast.Conditions, // the function body if postConditions != nil { - rewriteResult := checker.rewritePostConditions(*postConditions) + postConditionsPos = postConditions.StartPos + + rewriteResult := checker.rewritePostConditions(postConditions.Conditions) rewrittenPostConditions = &rewriteResult checker.Elaboration.SetPostConditionsRewrite(postConditions, rewriteResult) @@ -384,85 +393,96 @@ func (checker *Checker) visitWithPostConditions(postConditions *ast.Conditions, // TODO: improve: only declare when a condition actually refers to `before`? if postConditions != nil && - len(*postConditions) > 0 { + len(postConditions.Conditions) > 0 { checker.declareBefore() } - // If there is a return type, declare the constant `result`. - // If it is a resource type, the constant has the same type as a referecne to the return type. - // If it is not a resource type, the constant has the same type as the return type. + if rewrittenPostConditions != nil { - if returnType != VoidType { - var resultType Type - if returnType.IsResourceType() { + // If there is a return type, declare the constant `result`. + // If it is a resource type, the constant has the same type as a reference to the return type. + // If it is not a resource type, the constant has the same type as the return type. - innerType := returnType - optType, isOptional := returnType.(*OptionalType) - if isOptional { - innerType = optType.Type - } + if returnType != VoidType { + var resultType Type + if returnType.IsResourceType() { - auth := UnauthorizedAccess - // reference is authorized to the entire resource, since it is only accessible in a function where a resource value is owned. - // To create a "fully authorized" reference, we scan the resource type and produce a conjunction of all the entitlements mentioned within. - // So, for example, - // - // resource R { - // access(E) let x: Int - // access(X | Y) fun foo() {} - // } - // - // fun test(): @R { - // post { - // // do something with result here - // } - // return <- create R() - // } - // - // here the `result` value in the `post` block will have type `auth(E, X, Y) &R` - if entitlementSupportingType, ok := innerType.(EntitlementSupportingType); ok { - supportedEntitlements := entitlementSupportingType.SupportedEntitlements() - auth = supportedEntitlements.Access() - } + innerType := returnType + optType, isOptional := returnType.(*OptionalType) + if isOptional { + innerType = optType.Type + } - resultType = &ReferenceType{ - Type: innerType, - Authorization: auth, - } + auth := UnauthorizedAccess + // reference is authorized to the entire resource, + // since it is only accessible in a function where a resource value is owned. + // To create a "fully authorized" reference, + // we scan the resource type and produce a conjunction of all the entitlements mentioned within. + // So, for example, + // + // resource R { + // access(E) let x: Int + // access(X | Y) fun foo() {} + // } + // + // fun test(): @R { + // post { + // // do something with result here + // } + // return <- create R() + // } + // + // Here, the `result` value in the `post` block will have type `auth(E, X, Y) &R`. + + if entitlementSupportingType, ok := innerType.(EntitlementSupportingType); ok { + supportedEntitlements := entitlementSupportingType.SupportedEntitlements() + auth = supportedEntitlements.Access() + } + + resultType = &ReferenceType{ + Type: innerType, + Authorization: auth, + } - if isOptional { - // If the return type is an optional type T?, then create an optional reference (&T)?. - resultType = &OptionalType{ - Type: resultType, + if isOptional { + // If the return type is an optional type T?, then create an optional reference (&T)?. + resultType = &OptionalType{ + Type: resultType, + } } + } else { + resultType = returnType } - } else { - resultType = returnType + + checker.declareResultVariable( + resultType, + returnTypePos, + postConditionsPos, + ) } - checker.declareResult(resultType) - } - if rewrittenPostConditions != nil { checker.visitConditions(rewrittenPostConditions.RewrittenPostConditions) } } func (checker *Checker) visitFunctionBlock( functionBlock *ast.FunctionBlock, - returnTypeAnnotation TypeAnnotation, + returnType Type, + returnTypePos ast.HasPosition, checkResourceLoss bool, ) { checker.enterValueScope() defer checker.leaveValueScope(functionBlock.EndPosition, checkResourceLoss) if functionBlock.PreConditions != nil { - checker.visitConditions(*functionBlock.PreConditions) + checker.visitConditions(functionBlock.PreConditions.Conditions) } checker.visitWithPostConditions( functionBlock.PostConditions, - returnTypeAnnotation.Type, + returnType, + returnTypePos, func() { // NOTE: not checking block as it enters a new scope // and post-conditions need to be able to refer to block's declarations @@ -472,7 +492,31 @@ func (checker *Checker) visitFunctionBlock( ) } -func (checker *Checker) declareResult(ty Type) { +func (checker *Checker) declareResultVariable( + ty Type, + returnTypePos ast.HasPosition, + postConditionsPos ast.Position, +) { + existingVariable := checker.valueActivations.Current().Find(ResultIdentifier) + if existingVariable != nil { + checker.report( + &ResultVariableConflictError{ + Kind: existingVariable.DeclarationKind, + Pos: *existingVariable.Pos, + ReturnTypeRange: ast.NewRangeFromPositioned( + checker.memoryGauge, + returnTypePos, + ), + PostConditionsRange: ast.NewRange( + checker.memoryGauge, + postConditionsPos, + postConditionsPos.Shifted(checker.memoryGauge, len(ast.ConditionKindPost.Keyword())-1), + ), + }, + ) + return + } + _, err := checker.valueActivations.declareImplicitConstant( ResultIdentifier, ty, diff --git a/runtime/sema/check_transaction_declaration.go b/runtime/sema/check_transaction_declaration.go index bc86ffc347..d12b23bdd0 100644 --- a/runtime/sema/check_transaction_declaration.go +++ b/runtime/sema/check_transaction_declaration.go @@ -66,12 +66,13 @@ func (checker *Checker) VisitTransactionDeclaration(declaration *ast.Transaction checker.visitTransactionPrepareFunction(declaration.Prepare, transactionType, fieldMembers) if declaration.PreConditions != nil { - checker.visitConditions(*declaration.PreConditions) + checker.visitConditions(declaration.PreConditions.Conditions) } checker.visitWithPostConditions( declaration.PostConditions, VoidType, + nil, func() { checker.withSelfResourceInvalidationAllowed(func() { checker.visitTransactionExecuteFunction(declaration.Execute, transactionType) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 64c693802a..25461d4f39 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -4809,3 +4809,76 @@ func (*NestedReferenceError) IsUserError() {} func (e *NestedReferenceError) Error() string { return fmt.Sprintf("cannot create a nested reference to value of type %s", e.Type.QualifiedString()) } + +// ResultVariableConflictError + +type ResultVariableConflictError struct { + Kind common.DeclarationKind + Pos ast.Position + ReturnTypeRange ast.Range + PostConditionsRange ast.Range +} + +var _ SemanticError = &ResultVariableConflictError{} +var _ errors.UserError = &ResultVariableConflictError{} +var _ errors.SecondaryError = &ResultVariableConflictError{} + +func (*ResultVariableConflictError) isSemanticError() {} + +func (*ResultVariableConflictError) IsUserError() {} + +func (e *ResultVariableConflictError) Error() string { + return fmt.Sprintf( + "cannot declare %[1]s `%[2]s`: it conflicts with the `%[2]s` variable for the post-conditions", + e.Kind.Name(), + ResultIdentifier, + ) +} + +func (*ResultVariableConflictError) SecondaryError() string { + return "consider renaming the variable" +} + +func (e *ResultVariableConflictError) StartPosition() ast.Position { + return e.Pos +} + +func (e *ResultVariableConflictError) EndPosition(memoryGauge common.MemoryGauge) ast.Position { + length := len(ResultIdentifier) + return e.Pos.Shifted(memoryGauge, length-1) +} + +func (e *ResultVariableConflictError) ErrorNotes() []errors.ErrorNote { + return []errors.ErrorNote{ + ResultVariableReturnTypeNote{ + Range: e.ReturnTypeRange, + }, + ResultVariablePostConditionsNote{ + Range: e.PostConditionsRange, + }, + } +} + +// ResultVariableReturnTypeNote + +type ResultVariableReturnTypeNote struct { + ast.Range +} + +var _ errors.ErrorNote = ResultVariableReturnTypeNote{} + +func (ResultVariableReturnTypeNote) Message() string { + return "non-Void return type declared here" +} + +// ResultVariablePostConditionsNote + +type ResultVariablePostConditionsNote struct { + ast.Range +} + +var _ errors.ErrorNote = ResultVariablePostConditionsNote{} + +func (ResultVariablePostConditionsNote) Message() string { + return "post-conditions declared here" +} diff --git a/runtime/sema/post_conditions_rewrite.go b/runtime/sema/post_conditions_rewrite.go index 963d2dd99f..87d21f27e7 100644 --- a/runtime/sema/post_conditions_rewrite.go +++ b/runtime/sema/post_conditions_rewrite.go @@ -24,5 +24,5 @@ import ( type PostConditionsRewrite struct { BeforeStatements []ast.Statement - RewrittenPostConditions ast.Conditions + RewrittenPostConditions []ast.Condition } diff --git a/runtime/tests/checker/conditions_test.go b/runtime/tests/checker/conditions_test.go index 74979ccc91..04dbff6706 100644 --- a/runtime/tests/checker/conditions_test.go +++ b/runtime/tests/checker/conditions_test.go @@ -696,7 +696,7 @@ func TestCheckInvalidFunctionWithReturnTypeAndLocalResultAndPostConditionWithRes errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.RedeclarationError{}, errs[0]) + assert.IsType(t, &sema.ResultVariableConflictError{}, errs[0]) }) t.Run("emit condition", func(t *testing.T) { @@ -716,7 +716,7 @@ func TestCheckInvalidFunctionWithReturnTypeAndLocalResultAndPostConditionWithRes errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.RedeclarationError{}, errs[0]) + assert.IsType(t, &sema.ResultVariableConflictError{}, errs[0]) }) } @@ -738,7 +738,7 @@ func TestCheckInvalidFunctionWithReturnTypeAndResultParameterAndPostConditionWit errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.RedeclarationError{}, errs[0]) + assert.IsType(t, &sema.ResultVariableConflictError{}, errs[0]) }) t.Run("test condition", func(t *testing.T) { @@ -757,7 +757,7 @@ func TestCheckInvalidFunctionWithReturnTypeAndResultParameterAndPostConditionWit errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.RedeclarationError{}, errs[0]) + assert.IsType(t, &sema.ResultVariableConflictError{}, errs[0]) }) } diff --git a/runtime/tests/checker/function_test.go b/runtime/tests/checker/function_test.go index e1a431a81e..cc9acd69bc 100644 --- a/runtime/tests/checker/function_test.go +++ b/runtime/tests/checker/function_test.go @@ -376,7 +376,7 @@ func TestCheckInvalidResourceCapturingJustMemberAccess(t *testing.T) { assert.IsType(t, &sema.ResourceCapturingError{}, errs[0]) } -func TestCheckInvalidFunctionWithResult(t *testing.T) { +func TestCheckFunctionWithResult(t *testing.T) { t.Parallel() @@ -386,10 +386,26 @@ func TestCheckInvalidFunctionWithResult(t *testing.T) { return result } `) + require.NoError(t, err) +} + +func TestCheckInvalidFunctionWithResultAndPostCondition(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + fun test(): Int { + post { + result == 0 + } + let result = 0 + return result + } + `) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.RedeclarationError{}, errs[0]) + assert.IsType(t, &sema.ResultVariableConflictError{}, errs[0]) } func TestCheckFunctionNonExistingField(t *testing.T) { From fea8a9a739b31d55761018a0306b1df29d89d500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 15 Oct 2024 16:38:17 -0700 Subject: [PATCH 2/3] add test for inherited post-condition --- runtime/runtime_test.go | 88 +++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index b6d6cdf629..b7b7e87b0e 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -11485,25 +11485,81 @@ func TestResultRedeclared(t *testing.T) { t.Parallel() - runtime := NewTestInterpreterRuntime() + t.Run("function", func(t *testing.T) { - script := []byte(` - access(all) fun main(): Int { let result = 1; return result } - `) + t.Parallel() - runtimeInterface := &TestRuntimeInterface{} + runtime := NewTestInterpreterRuntime() - nextScriptLocation := NewScriptLocationGenerator() + script := []byte(` + access(all) fun main(): Int { + let result = 1 + return result + } + `) - _, err := runtime.ExecuteScript( - Script{ - Source: script, - }, - Context{ - Interface: runtimeInterface, - Location: nextScriptLocation(), - }, - ) - require.NoError(t, err) + runtimeInterface := &TestRuntimeInterface{} + + nextScriptLocation := NewScriptLocationGenerator() + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + }) + + t.Run("method with inherited post-condition using result", func(t *testing.T) { + + t.Parallel() + + runtime := NewTestInterpreterRuntime() + + script := []byte(` + access(all) + struct interface SI { + access(all) + fun test(): Int { + post { + result == 1 + } + } + } + + access(all) + struct S: SI { + + access(all) + fun test(): Int { + let result = 1 + return result + } + } + + access(all) fun main(): Int { + return S().test() + } + `) + + runtimeInterface := &TestRuntimeInterface{} + + nextScriptLocation := NewScriptLocationGenerator() + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + }) } From 5412bfce4a53104723288998b82c91c91278e379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 16 Oct 2024 09:53:38 -0700 Subject: [PATCH 3/3] Update runtime/runtime_test.go Co-authored-by: Supun Setunga --- runtime/runtime_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index b7b7e87b0e..d3f13fa003 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -11526,7 +11526,7 @@ func TestResultRedeclared(t *testing.T) { access(all) fun test(): Int { post { - result == 1 + result == 2 } } } @@ -11537,7 +11537,7 @@ func TestResultRedeclared(t *testing.T) { access(all) fun test(): Int { let result = 1 - return result + return 2 // return a different value than the local variable } }