From ba580e2efe61fae362b86e65a0b17c2202aabb0c Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Thu, 10 Oct 2024 11:24:10 -0700 Subject: [PATCH 1/5] Remove identifier constraint, add tests. --- interpreter/misc_test.go | 49 +++++++++++++++++++++++++++++++++++++++ parser/expression.go | 4 ---- parser/expression_test.go | 26 ++++----------------- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/interpreter/misc_test.go b/interpreter/misc_test.go index 8ec9d08532..ee548f85b8 100644 --- a/interpreter/misc_test.go +++ b/interpreter/misc_test.go @@ -12509,4 +12509,53 @@ func TestInterpretStringTemplates(t *testing.T) { inter.Globals.Get("x").GetValue(inter), ) }) + + t.Run("func", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + let add = fun(): Int { + return 2+2 + } + let x: String = "\(add())" + `) + + AssertValuesEqual( + t, + inter, + interpreter.NewUnmeteredStringValue("4"), + inter.Globals.Get("x").GetValue(inter), + ) + }) + + t.Run("ternary", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + let z = false + let x: String = "\(z ? "foo" : "bar" )" + `) + + AssertValuesEqual( + t, + inter, + interpreter.NewUnmeteredStringValue("bar"), + inter.Globals.Get("x").GetValue(inter), + ) + }) + + t.Run("nested", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + let x: String = "\(2*(4-2) + 1 == 5)" + `) + + AssertValuesEqual( + t, + inter, + interpreter.NewUnmeteredStringValue("true"), + inter.Globals.Get("x").GetValue(inter), + ) + }) } diff --git a/parser/expression.go b/parser/expression.go index 8499f95e60..83b11d616b 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -1191,10 +1191,6 @@ func defineStringExpression() { if err != nil { return nil, err } - // limit string templates to identifiers only - if _, ok := value.(*ast.IdentifierExpression); !ok { - return nil, p.syntaxError("expected identifier got: %s", value.String()) - } _, err = p.mustOne(lexer.TokenParenClose) if err != nil { return nil, err diff --git a/parser/expression_test.go b/parser/expression_test.go index 172884de18..6de08c4f78 100644 --- a/parser/expression_test.go +++ b/parser/expression_test.go @@ -6185,7 +6185,7 @@ func TestParseStringTemplate(t *testing.T) { ) }) - t.Run("invalid, num", func(t *testing.T) { + t.Run("valid, num", func(t *testing.T) { t.Parallel() @@ -6200,16 +6200,7 @@ func TestParseStringTemplate(t *testing.T) { } } - require.Error(t, err) - AssertEqualWithDiff(t, - []error{ - &SyntaxError{ - Message: "expected identifier got: 2 + 2", - Pos: ast.Position{Offset: 13, Line: 2, Column: 12}, - }, - }, - errs, - ) + require.NoError(t, err) }) t.Run("valid, nested identifier", func(t *testing.T) { @@ -6278,7 +6269,7 @@ func TestParseStringTemplate(t *testing.T) { ) }) - t.Run("invalid, function identifier", func(t *testing.T) { + t.Run("valid, function identifier", func(t *testing.T) { t.Parallel() @@ -6293,16 +6284,7 @@ func TestParseStringTemplate(t *testing.T) { } } - require.Error(t, err) - AssertEqualWithDiff(t, - []error{ - &SyntaxError{ - Message: "expected identifier got: add()", - Pos: ast.Position{Offset: 12, Line: 2, Column: 11}, - }, - }, - errs, - ) + require.NoError(t, err) }) t.Run("invalid, unbalanced paren", func(t *testing.T) { From d93bb65bebf466512f9d2a2b6a0b801e78bfdaf6 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Thu, 10 Oct 2024 11:49:09 -0700 Subject: [PATCH 2/5] Add more tests for expressions in string templates. --- parser/expression_test.go | 69 ++++++++++++++++++++++++++++++++++++++- sema/string_test.go | 35 ++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/parser/expression_test.go b/parser/expression_test.go index 6de08c4f78..caaf70a653 100644 --- a/parser/expression_test.go +++ b/parser/expression_test.go @@ -34,6 +34,7 @@ import ( "github.com/onflow/cadence/common" "github.com/onflow/cadence/errors" "github.com/onflow/cadence/parser/lexer" + "github.com/onflow/cadence/runtime/tests/utils" . "github.com/onflow/cadence/test_utils/common_utils" ) @@ -6287,7 +6288,7 @@ func TestParseStringTemplate(t *testing.T) { require.NoError(t, err) }) - t.Run("invalid, unbalanced paren", func(t *testing.T) { + t.Run("invalid, missing paren", func(t *testing.T) { t.Parallel() @@ -6314,6 +6315,33 @@ func TestParseStringTemplate(t *testing.T) { ) }) + t.Run("invalid, nested expression paren", func(t *testing.T) { + + t.Parallel() + + _, errs := testParseExpression(` + "\((2+2)/2()" + `) + + var err error + if len(errs) > 0 { + err = Error{ + Errors: errs, + } + } + + require.Error(t, err) + utils.AssertEqualWithDiff(t, + []error{ + &SyntaxError{ + Message: "expected token ')'", + Pos: ast.Position{Offset: 16, Line: 2, Column: 15}, + }, + }, + errs, + ) + }) + t.Run("invalid, nested templates", func(t *testing.T) { t.Parallel() @@ -6478,6 +6506,45 @@ func TestParseStringTemplate(t *testing.T) { AssertEqualWithDiff(t, expected, actual) }) + + t.Run("valid, extra closing paren", func(t *testing.T) { + + t.Parallel() + + actual, errs := testParseExpression(` + "\(a))" + `) + + var err error + if len(errs) > 0 { + err = Error{ + Errors: errs, + } + } + + require.NoError(t, err) + + expected := &ast.StringTemplateExpression{ + Values: []string{ + "", + ")", + }, + Expressions: []ast.Expression{ + &ast.IdentifierExpression{ + Identifier: ast.Identifier{ + Identifier: "a", + Pos: ast.Position{Offset: 7, Line: 2, Column: 6}, + }, + }, + }, + Range: ast.Range{ + StartPos: ast.Position{Offset: 4, Line: 2, Column: 3}, + EndPos: ast.Position{Offset: 10, Line: 2, Column: 9}, + }, + } + + utils.AssertEqualWithDiff(t, expected, actual) + }) } func TestParseNilCoalescing(t *testing.T) { diff --git a/sema/string_test.go b/sema/string_test.go index cc46bc786d..578f81d194 100644 --- a/sema/string_test.go +++ b/sema/string_test.go @@ -743,6 +743,27 @@ func TestCheckStringTemplate(t *testing.T) { assert.IsType(t, &sema.TypeMismatchWithDescriptionError{}, errs[0]) }) + t.Run("invalid, struct with tostring", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + access(all) + struct SomeStruct { + access(all) + view fun toString(): String { + return "SomeStruct" + } + } + let a = SomeStruct() + let x: String = "\(a)" + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.TypeMismatchWithDescriptionError{}, errs[0]) + }) + t.Run("invalid, array", func(t *testing.T) { t.Parallel() @@ -788,4 +809,18 @@ func TestCheckStringTemplate(t *testing.T) { assert.IsType(t, &sema.TypeMismatchWithDescriptionError{}, errs[0]) }) + + t.Run("invalid, expression type", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + let y: Int = 0 + let x: String = "\(y > 0 ? "String" : true)" + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.TypeMismatchWithDescriptionError{}, errs[0]) + }) } From ce4bb7456f0a1f927ca246d614aa387187184741 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Wed, 30 Oct 2024 17:23:02 -0400 Subject: [PATCH 3/5] Update tests. --- parser/expression_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parser/expression_test.go b/parser/expression_test.go index caaf70a653..f8c44fe1c0 100644 --- a/parser/expression_test.go +++ b/parser/expression_test.go @@ -34,7 +34,6 @@ import ( "github.com/onflow/cadence/common" "github.com/onflow/cadence/errors" "github.com/onflow/cadence/parser/lexer" - "github.com/onflow/cadence/runtime/tests/utils" . "github.com/onflow/cadence/test_utils/common_utils" ) @@ -6331,7 +6330,7 @@ func TestParseStringTemplate(t *testing.T) { } require.Error(t, err) - utils.AssertEqualWithDiff(t, + AssertEqualWithDiff(t, []error{ &SyntaxError{ Message: "expected token ')'", @@ -6543,7 +6542,7 @@ func TestParseStringTemplate(t *testing.T) { }, } - utils.AssertEqualWithDiff(t, expected, actual) + AssertEqualWithDiff(t, expected, actual) }) } From fab7567387937f41a694f7d2b625e5f0f34e9237 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Wed, 13 Nov 2024 15:56:34 -0500 Subject: [PATCH 4/5] Simplify error checking. --- parser/expression_test.go | 54 +++++---------------------------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/parser/expression_test.go b/parser/expression_test.go index f8c44fe1c0..fa51090704 100644 --- a/parser/expression_test.go +++ b/parser/expression_test.go @@ -6139,14 +6139,7 @@ func TestParseStringTemplate(t *testing.T) { "this is a test \(FOO) `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ @@ -6166,14 +6159,7 @@ func TestParseStringTemplate(t *testing.T) { "\(.)" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ @@ -6250,14 +6236,7 @@ func TestParseStringTemplate(t *testing.T) { "\()" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ @@ -6295,14 +6274,7 @@ func TestParseStringTemplate(t *testing.T) { "\(add" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ @@ -6322,14 +6294,7 @@ func TestParseStringTemplate(t *testing.T) { "\((2+2)/2()" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ @@ -6349,14 +6314,7 @@ func TestParseStringTemplate(t *testing.T) { "outer string \( "\(inner template)" )" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.Error(t, err) + require.NotEmpty(t, errs) AssertEqualWithDiff(t, []error{ &SyntaxError{ From 7e354806b9fe4b998211890621e9bd68fe155c43 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Thu, 14 Nov 2024 09:50:17 -0500 Subject: [PATCH 5/5] Clean up valid tests for string templates. --- parser/expression_test.go | 81 +++++---------------------------------- 1 file changed, 9 insertions(+), 72 deletions(-) diff --git a/parser/expression_test.go b/parser/expression_test.go index fa51090704..566c9e39a2 100644 --- a/parser/expression_test.go +++ b/parser/expression_test.go @@ -6054,14 +6054,7 @@ func TestParseStringTemplate(t *testing.T) { "\(test)" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6093,14 +6086,7 @@ func TestParseStringTemplate(t *testing.T) { "this is a test \(abc)\(def) test" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6179,14 +6165,7 @@ func TestParseStringTemplate(t *testing.T) { "\(2 + 2) is a" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) }) t.Run("valid, nested identifier", func(t *testing.T) { @@ -6197,14 +6176,7 @@ func TestParseStringTemplate(t *testing.T) { "\((a))" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6256,14 +6228,7 @@ func TestParseStringTemplate(t *testing.T) { "\(add())" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) }) t.Run("invalid, missing paren", func(t *testing.T) { @@ -6334,14 +6299,7 @@ func TestParseStringTemplate(t *testing.T) { "a\(b)c" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6373,14 +6331,7 @@ func TestParseStringTemplate(t *testing.T) { "\(a)b\(c)" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6419,14 +6370,7 @@ func TestParseStringTemplate(t *testing.T) { "\(a)\(b)\(c)" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{ @@ -6472,14 +6416,7 @@ func TestParseStringTemplate(t *testing.T) { "\(a))" `) - var err error - if len(errs) > 0 { - err = Error{ - Errors: errs, - } - } - - require.NoError(t, err) + require.Empty(t, errs) expected := &ast.StringTemplateExpression{ Values: []string{