Skip to content

Commit

Permalink
Fix quote handling at end of block string descriptions (#355)
Browse files Browse the repository at this point in the history
* test: add test demonstrating quotes at end of description bug

When a block string description ends with quotes immediately followed by
triple quotes, the parser fails with an "Unexpected <Invalid>" error.
This test demonstrates the issue by comparing a working case (with space
between the quote and closing triple quotes) and the failing case.

* fix: properly handle quote characters at end of block string descriptions

The lexer was incorrectly handling the case where a quote character (")
appeared immediately before the closing triple quotes (""").

The fix changes how the lexer identifies closing triple quotes:
- When a quote character is encountered, we now count consecutive quotes
- If there are 3 or more quotes in a row, we treat the last 3 as the closing triple quote
- Any extra quotes (beyond the closing 3) are added to the string content

This handles cases like:
- """This is a "test"""" (quote followed by closing triple quotes)
- """This is a ""test""""" (multiple quotes followed by closing triple quotes)
  • Loading branch information
clinta authored Feb 27, 2025
1 parent 94fb1f6 commit 135eef6
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 9 deletions.
30 changes: 21 additions & 9 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,17 +421,29 @@ func (s *Lexer) readBlockString() (Token, error) {
r := s.Input[s.end]

// Closing triple quote (""")
if r == '"' && s.end+3 <= inputLen && s.Input[s.end:s.end+3] == `"""` {
t, err := s.makeValueToken(BlockString, blockStringValue(buf.String()))
if r == '"' {
// Count consecutive quotes
quoteCount := 1
i := s.end + 1
for i < inputLen && s.Input[i] == '"' {
quoteCount++
i++
}

// the token should not include the quotes in its value, but should cover them in its position
t.Pos.Start -= 3
t.Pos.End += 3
// If we have at least 3 quotes, use the last 3 as the closing quote
if quoteCount >= 3 {
// Add any extra quotes to the buffer (except the last 3)
for j := 0; j < quoteCount-3; j++ {
buf.WriteByte('"')
}

// skip the close quote
s.end += 3
s.endRunes += 3
return t, err
t, err := s.makeValueToken(BlockString, blockStringValue(buf.String()))
t.Pos.Start -= 3
t.Pos.End += 3
s.end += quoteCount
s.endRunes += quoteCount
return t, err
}
}

// SourceCharacter
Expand Down
27 changes: 27 additions & 0 deletions validator/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,30 @@ func TestSchemaDescription(t *testing.T) {
want := "A simple GraphQL schema which is well described."
require.Equal(t, want, s.Description)
}

func TestSchemaDescriptionWithQuotesAtEnd(t *testing.T) {
// This test demonstrates a bug in the parser where quotes at the end of a
// description without a space cause parsing errors

t.Run("working case - quotes followed by space at end of description", func(t *testing.T) {
// This case works correctly - note the space after the quote and before the closing """
_, err := LoadSchema(Prelude, &ast.Source{Name: "test", Input: `
"""This is a "test" """
type Query {
field: String
}
`, BuiltIn: false})
require.NoError(t, err, "Schema with quotes followed by space at end of description should parse successfully")
})

t.Run("bug - quotes at end of description", func(t *testing.T) {
// This case fails - note the quote directly before the closing """
_, err := LoadSchema(Prelude, &ast.Source{Name: "test", Input: `
"""This is a "test""""
type Query {
field: String
}
`, BuiltIn: false})
require.NoError(t, err, "Schema with quotes at end of description should parse successfully")
})
}

0 comments on commit 135eef6

Please sign in to comment.