Skip to content

fix: correctly handle even numbers of backslashes before closing string quote#427

Merged
jeffijoe merged 3 commits into
jeffijoe:masterfrom
chatman-media:fix/skipstring-even-backslashes
Jun 15, 2026
Merged

fix: correctly handle even numbers of backslashes before closing string quote#427
jeffijoe merged 3 commits into
jeffijoe:masterfrom
chatman-media:fix/skipstring-even-backslashes

Conversation

@chatman-media

Copy link
Copy Markdown
Contributor

Problem

The skipString function in src/function-tokenizer.ts used a one-character look-behind to decide whether a closing quote is escaped:

const prev = source.charAt(pos - 1)
if (ch === quote && prev !== '\\') {
  pos++
  return
}

This check is incorrect when an even number of backslashes precedes the closing quote. For example, the source string 'test\\' represents the JS value test\ (a string ending with one literal backslash). Here the source characters are ' t e s t \ \ '. Because the character immediately before the closing ' is a backslash, the old code treats the quote as escaped and runs past the end of the string, ultimately throwing:

SyntaxError: Parsing parameter list, did not expect EOF token

This manifests in practice whenever a class constructor has a parameter with a string default value whose final character is a backslash (e.g. Windows filesystem paths):

class MyService {
  constructor(dep1, basePath = 'C:\\Windows\\', dep2) {}
}

Fix

Count all consecutive backslashes before the quote. An even count means each pair cancels out (literal backslash characters), so the quote is the end of the string. An odd count means the last backslash is an escape for the quote, so the string continues.

if (ch === quote) {
  let backslashCount = 0
  let i = pos - 1
  while (i >= 0 && source.charAt(i) === '\\') {
    backslashCount++
    i--
  }
  if (backslashCount % 2 === 0) {
    pos++
    return
  }
}

Tests

  • Added a new tokenizer test (can skip strings ending with an even number of backslashes) covering 2-backslash and 4-backslash cases.
  • Added a new param-parser bug regression test (string default ending with even backslashes) that directly exercises the real-world failure scenario, including a check that single-backslash escaped quotes still work correctly.

All 235 tests pass.

Comment on lines +61 to +64
// An even number of backslashes before the closing quote means the backslashes
// escape each other (e.g. `\\` = one literal backslash), so the quote is NOT escaped.
// The previous implementation only checked the immediately preceding character,
// causing it to incorrectly treat the closing quote as escaped.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the new comments from the test files

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed the comments from both test files in 769ea9d.

Comment thread src/function-tokenizer.ts Outdated
Comment on lines +218 to +221
// Checks if the quote was escaped by counting consecutive preceding backslashes.
// An even number of backslashes means they cancel each other out (e.g. `\\` is a
// literal backslash), so the quote is NOT escaped. An odd number means the quote
// IS escaped (e.g. `\'`).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't checking the immediate next character (and consuming it) if it's also a backslash work as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's cleaner — switched to exactly that in 769ea9d: when a backslash is encountered, both it and the following character are consumed (pos += 2), so an escaped quote never terminates the scan and a literal backslash can't swallow the closing quote. Behavior is identical (all snapshots unchanged, 235 tests pass).

Comment thread src/function-tokenizer.ts
@jeffijoe

Copy link
Copy Markdown
Owner

@chatman-media I'll need you to rebase this to latest master before I can merge it. Thanks!

…ng quote

The `skipString` function in the tokenizer checked only the immediately
preceding character to determine if a closing quote was escaped. This
caused it to treat `'\\'` (a string ending with a literal backslash) as
an unclosed string, because the closing quote's predecessor is a `\`.

The correct rule is to count all consecutive backslashes before the
quote: an even count means they escape each other (literal backslashes),
so the quote terminates the string; an odd count means the last
backslash escapes the quote.

Fixes resolution errors thrown when a class constructor has a string
default parameter value ending with an even number of backslashes, e.g.:

  class MyService {
    constructor(dep1, path = 'C:\\Windows\\', dep2) {}
  }
@chatman-media
chatman-media force-pushed the fix/skipstring-even-backslashes branch from 924b4a6 to 5056017 Compare June 15, 2026 08:58
@chatman-media

Copy link
Copy Markdown
Contributor Author

Done — rebased onto the latest master (now a clean 3-commit history on top of 8b45b70, no merge commit). The diff is just the tokenizer fix + tests/snapshot. npm ci and the tokenizer/param-parser suites pass locally. Ready when you are — thanks!

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 100.0%. remained the same — chatman-media:fix/skipstring-even-backslashes into jeffijoe:master

@jeffijoe
jeffijoe merged commit 7f1ed32 into jeffijoe:master Jun 15, 2026
5 checks passed
@jeffijoe

Copy link
Copy Markdown
Owner

Released as v13.0.5, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants