diff --git a/runtime/ast/comments.go b/runtime/ast/comments.go index c46743256..2daa66cde 100644 --- a/runtime/ast/comments.go +++ b/runtime/ast/comments.go @@ -3,6 +3,7 @@ package ast import ( "bytes" "github.com/onflow/cadence/runtime/common" + "strings" ) type Comments struct { @@ -17,6 +18,20 @@ func (c Comments) PackToList() []*Comment { return comments } +// LeadingDocString prints the leading doc comments to string +func (c Comments) LeadingDocString() string { + var s strings.Builder + for _, comment := range c.Leading { + if comment.Doc() { + if s.Len() > 0 { + s.WriteRune('\n') + } + s.Write(comment.Text()) + } + } + return s.String() +} + type Comment struct { source []byte } diff --git a/runtime/ast/expression.go b/runtime/ast/expression.go index 0142b92b2..992adf37e 100644 --- a/runtime/ast/expression.go +++ b/runtime/ast/expression.go @@ -227,6 +227,7 @@ type IntegerExpression struct { PositiveLiteral []byte Range Base int + Comments } var _ Element = &IntegerExpression{} @@ -238,6 +239,7 @@ func NewIntegerExpression( value *big.Int, base int, tokenRange Range, + comments Comments, ) *IntegerExpression { common.UseMemory(gauge, common.IntegerExpressionMemoryUsage) @@ -246,6 +248,7 @@ func NewIntegerExpression( Value: value, Base: base, Range: tokenRange, + Comments: comments, } } diff --git a/runtime/ast/function_declaration.go b/runtime/ast/function_declaration.go index e9d384c50..d5598077a 100644 --- a/runtime/ast/function_declaration.go +++ b/runtime/ast/function_declaration.go @@ -20,8 +20,6 @@ package ast import ( "encoding/json" - "strings" - "github.com/turbolent/prettier" "github.com/onflow/cadence/runtime/common" @@ -209,16 +207,7 @@ func (d *FunctionDeclaration) DeclarationMembers() *Members { } func (d *FunctionDeclaration) DeclarationDocString() string { - var s strings.Builder - for _, comment := range d.Comments.Leading { - if comment.Doc() { - if s.Len() > 0 { - s.WriteRune('\n') - } - s.Write(comment.Text()) - } - } - return s.String() + return d.Comments.LeadingDocString() } func (d *FunctionDeclaration) Doc() prettier.Doc { diff --git a/runtime/ast/variable_declaration.go b/runtime/ast/variable_declaration.go index bc82b55a5..43620b028 100644 --- a/runtime/ast/variable_declaration.go +++ b/runtime/ast/variable_declaration.go @@ -33,11 +33,11 @@ type VariableDeclaration struct { Transfer *Transfer SecondTransfer *Transfer ParentIfStatement *IfStatement `json:"-"` - DocString string Identifier Identifier StartPos Position `json:"-"` Access Access IsConstant bool + Comments } var _ Element = &VariableDeclaration{} @@ -55,7 +55,7 @@ func NewVariableDeclaration( startPos Position, secondTransfer *Transfer, secondValue Expression, - docString string, + comments Comments, ) *VariableDeclaration { common.UseMemory(gauge, common.VariableDeclarationMemoryUsage) @@ -69,7 +69,7 @@ func NewVariableDeclaration( StartPos: startPos, SecondTransfer: secondTransfer, SecondValue: secondValue, - DocString: docString, + Comments: comments, } } @@ -127,7 +127,7 @@ func (d *VariableDeclaration) DeclarationMembers() *Members { } func (d *VariableDeclaration) DeclarationDocString() string { - return d.DocString + return d.Comments.LeadingDocString() } var varKeywordDoc prettier.Doc = prettier.Text("var") diff --git a/runtime/old_parser/declaration.go b/runtime/old_parser/declaration.go index f3bd02c43..3e22a447a 100644 --- a/runtime/old_parser/declaration.go +++ b/runtime/old_parser/declaration.go @@ -415,7 +415,7 @@ func parseVariableDeclaration( startPos, secondTransfer, secondValue, - docString, + ast.Comments{}, ) castingExpression, leftIsCasting := value.(*ast.CastingExpression) diff --git a/runtime/old_parser/expression.go b/runtime/old_parser/expression.go index b4fd4c2a1..7a2f8638f 100644 --- a/runtime/old_parser/expression.go +++ b/runtime/old_parser/expression.go @@ -1771,7 +1771,7 @@ func parseIntegerLiteral(p *parser, literal, text []byte, kind common.IntegerLit value = new(big.Int) } - return ast.NewIntegerExpression(p.memoryGauge, literal, value, base, tokenRange) + return ast.NewIntegerExpression(p.memoryGauge, literal, value, base, tokenRange, ast.Comments{}) } func parseFixedPointPart(gauge common.MemoryGauge, part string) (integer *big.Int, scale uint) { diff --git a/runtime/parser/declaration.go b/runtime/parser/declaration.go index d5eabf942..8fa901994 100644 --- a/runtime/parser/declaration.go +++ b/runtime/parser/declaration.go @@ -102,7 +102,7 @@ func parseDeclaration(p *parser) (ast.Declaration, error) { if purity != ast.FunctionPurityUnspecified { return nil, NewSyntaxError(*purityPos, "invalid view modifier for variable") } - return parseVariableDeclaration(p, access, accessPos, docString) + return parseVariableDeclaration(p, access, accessPos) case KeywordFun: return parseFunctionDeclaration( @@ -512,10 +512,10 @@ func parseVariableDeclaration( p *parser, access ast.Access, accessPos *ast.Position, - docString string, ) (*ast.VariableDeclaration, error) { - startPos := p.current.StartPos + startToken := p.current + startPos := startToken.StartPos if accessPos != nil { startPos = *accessPos } @@ -525,6 +525,8 @@ func parseVariableDeclaration( // Skip the `let` or `var` keyword p.nextSemanticToken() + identifierToken := p.current + identifier, err := p.nonReservedIdentifier("after start of variable declaration") if err != nil { return nil, err @@ -546,6 +548,8 @@ func parseVariableDeclaration( } p.skipSpace() + + transferToken := p.current transfer := parseTransfer(p) if transfer == nil { return nil, p.syntaxError("expected transfer") @@ -578,7 +582,12 @@ func parseVariableDeclaration( startPos, secondTransfer, secondValue, - docString, + ast.Comments{ + Leading: append( + append(startToken.PackToList(), identifierToken.PackToList()...), + transferToken.PackToList()..., + ), + }, ) castingExpression, leftIsCasting := value.(*ast.CastingExpression) diff --git a/runtime/parser/declaration_test.go b/runtime/parser/declaration_test.go index 7f990bb29..84bde4008 100644 --- a/runtime/parser/declaration_test.go +++ b/runtime/parser/declaration_test.go @@ -72,6 +72,59 @@ func TestParseVariableDeclaration(t *testing.T) { ) }) + t.Run("var, no type annotation, copy, one value, comments", func(t *testing.T) { + + t.Parallel() + + result, errs := testParseDeclarations(` +// Before x +var x = /* Before 1 */ 1 // After 1 +// Ignored +`) + require.Empty(t, errs) + + utils.AssertEqualWithDiff(t, + []ast.Declaration{ + &ast.VariableDeclaration{ + Access: ast.AccessNotSpecified, + IsConstant: false, + Identifier: ast.Identifier{ + Identifier: "x", + Pos: ast.Position{Line: 3, Column: 4, Offset: 17}, + }, + Comments: ast.Comments{ + Leading: []*ast.Comment{ + ast.NewComment(nil, []byte("// Before x")), + ast.NewComment(nil, []byte("/* Before 1 */")), + }, + Trailing: []*ast.Comment{}, + }, + Value: &ast.IntegerExpression{ + PositiveLiteral: []byte("1"), + Value: big.NewInt(1), + Base: 10, + Range: ast.Range{ + StartPos: ast.Position{Line: 3, Column: 23, Offset: 36}, + EndPos: ast.Position{Line: 3, Column: 23, Offset: 36}, + }, + Comments: ast.Comments{ + Leading: []*ast.Comment{}, + Trailing: []*ast.Comment{ + ast.NewComment(nil, []byte("// After 1")), + }, + }, + }, + Transfer: &ast.Transfer{ + Operation: ast.TransferOperationCopy, + Pos: ast.Position{Line: 3, Column: 6, Offset: 19}, + }, + StartPos: ast.Position{Line: 3, Column: 0, Offset: 13}, + }, + }, + result, + ) + }) + t.Run("var, no type annotation, copy, one value, access(all)", func(t *testing.T) { t.Parallel() @@ -2493,7 +2546,7 @@ func TestParseEvent(t *testing.T) { result, errs := testParseDeclarations(` // Before E event E() // After E -// Should be ignored +// Ignored `) require.Empty(t, errs) diff --git a/runtime/parser/expression.go b/runtime/parser/expression.go index 87db3ed91..8834d05e1 100644 --- a/runtime/parser/expression.go +++ b/runtime/parser/expression.go @@ -360,7 +360,7 @@ func init() { literal, literal[2:], common.IntegerLiteralKindBinary, - token.Range, + token, ), nil }, }) @@ -374,7 +374,7 @@ func init() { literal, literal[2:], common.IntegerLiteralKindOctal, - token.Range, + token, ), nil }, }) @@ -388,7 +388,7 @@ func init() { literal, literal, common.IntegerLiteralKindDecimal, - token.Range, + token, ), nil }, }) @@ -402,7 +402,7 @@ func init() { literal, literal[2:], common.IntegerLiteralKindHexadecimal, - token.Range, + token, ), nil }, }) @@ -416,7 +416,7 @@ func init() { literal, literal[2:], common.IntegerLiteralKindUnknown, - token.Range, + token, ), nil }, }) @@ -1730,7 +1730,7 @@ func parseHex(r rune) rune { return -1 } -func parseIntegerLiteral(p *parser, literal, text []byte, kind common.IntegerLiteralKind, tokenRange ast.Range) *ast.IntegerExpression { +func parseIntegerLiteral(p *parser, literal, text []byte, kind common.IntegerLiteralKind, token lexer.Token) *ast.IntegerExpression { report := func(invalidKind InvalidNumberLiteralKind) { p.report( @@ -1739,7 +1739,7 @@ func parseIntegerLiteral(p *parser, literal, text []byte, kind common.IntegerLit InvalidIntegerLiteralKind: invalidKind, // NOTE: not using text, because it has the base-prefix stripped Literal: string(literal), - Range: tokenRange, + Range: token.Range, }, ) } @@ -1789,7 +1789,7 @@ func parseIntegerLiteral(p *parser, literal, text []byte, kind common.IntegerLit value = new(big.Int) } - return ast.NewIntegerExpression(p.memoryGauge, literal, value, base, tokenRange) + return ast.NewIntegerExpression(p.memoryGauge, literal, value, base, token.Range, token.Comments) } func parseFixedPointPart(gauge common.MemoryGauge, part string) (integer *big.Int, scale uint) { diff --git a/runtime/parser/statement.go b/runtime/parser/statement.go index 7a66d4a3e..6697f9490 100644 --- a/runtime/parser/statement.go +++ b/runtime/parser/statement.go @@ -311,7 +311,7 @@ func parseIfStatement(p *parser) (*ast.IfStatement, error) { switch string(p.currentTokenSource()) { case KeywordLet, KeywordVar: variableDeclaration, err = - parseVariableDeclaration(p, ast.AccessNotSpecified, nil, "") + parseVariableDeclaration(p, ast.AccessNotSpecified, nil) if err != nil { return nil, err } diff --git a/runtime/sema/check_variable_declaration.go b/runtime/sema/check_variable_declaration.go index b45141626..fb26450aa 100644 --- a/runtime/sema/check_variable_declaration.go +++ b/runtime/sema/check_variable_declaration.go @@ -225,7 +225,7 @@ func (checker *Checker) declareVariableDeclaration(declaration *ast.VariableDecl variable, err := checker.valueActivations.declare(variableDeclaration{ identifier: identifier, ty: declarationType, - docString: declaration.DocString, + docString: declaration.DeclarationDocString(), access: checker.accessFromAstAccess(declaration.Access), kind: declaration.DeclarationKind(), pos: declaration.Identifier.Pos, diff --git a/runtime/sema/positioninfo.go b/runtime/sema/positioninfo.go index 9b0a8a568..53f4fb0c0 100644 --- a/runtime/sema/positioninfo.go +++ b/runtime/sema/positioninfo.go @@ -246,7 +246,7 @@ func (i *PositionInfo) recordVariableDeclarationRange( Identifier: identifier, DeclarationKind: declaration.DeclarationKind(), Type: declarationType, - DocString: declaration.DocString, + DocString: declaration.DeclarationDocString(), }, ) } diff --git a/tools/maprange/go.sum b/tools/maprange/go.sum index 05680b312..7a9273fb4 100644 --- a/tools/maprange/go.sum +++ b/tools/maprange/go.sum @@ -1,6 +1,11 @@ +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=