Skip to content

fix: avoid gluing a slash onto a comment in dialects where // starts a line comment - #977

Open
Jeremy-xuan wants to merge 1 commit into
sql-formatter-org:masterfrom
Jeremy-xuan:fix/slash-before-comment
Open

Jeremy-xuan wants to merge 1 commit into
sql-formatter-org:masterfrom
Jeremy-xuan:fix/slash-before-comment

Conversation

@Jeremy-xuan

Copy link
Copy Markdown

fix: avoid gluing a slash onto a comment in dialects where // starts a line comment

What's wrong

Snowflake is the only dialect where // starts a line comment. With
denseOperators on, a / operator directly in front of a comment gets glued to it,
forming // — so the rest of the line becomes comment text:

SELECT a / /* c */ b FROM t;
SELECT
  a//* c */ b
FROM
  t;

b is now inside the comment. The statement quietly means something else, and it
does not look wrong enough to catch by eye. A /* sql-formatter-disable */ marker
is swallowed the same way, so the user's "do not touch this" instruction stops
working:

SELECT a / /* sql-formatter-disable */ b FROM t;   --  ... a//* sql-formatter-disable */ b ...

Root cause

Layout.add() already had this exact guard for --:

  // Don't glue a layout item starting with "-" directly onto one ending with
  // "-": that forms "--", which re-parses as a line comment and
  // swallows the rest of the line (e.g. densing "a - -b" into "a--b").
  if (item.startsWith('-') && this.lastItemEndsWith('-')) {
    this.items.push(WS.SPACE);
  }

The reasoning is right, but it is hard-coded to --. Every other line comment
marker a dialect uses has the same hazard, and // is one of them.

The fix

The guard now asks the dialect for its own line comment markers instead of assuming
--. The markers already exist on the tokenizer options, so they are carried through
the same path as identifierDashes:

  • ProcessedDialectFormatOptions gains lineCommentTypes
  • dialect.ts fills it from tokenizerOptions.lineCommentTypes ?? ['--']
  • Layout takes it as a constructor argument and checks it in wouldFormLineComment()
  private wouldFormLineComment(item: string): boolean {
    const lastItem = last(this.items);
    if (typeof lastItem !== 'string') {
      return false;
    }
    return this.lineCommentTypes.some(
      (marker) =>
        marker.length > 1 && lastItem.endsWith(marker[0]) && item.startsWith(marker.slice(1))
    );
  }

Behavior for -- is unchanged — the existing test does not glue a "-" in front of another "-" in dense mode still passes — and dialects that do not use // are not
affected. lastItemEndsWith() was left with no callers, so it is gone.

Tests

Three tests in test/snowflake.test.ts: a block comment after /, a
sql-formatter-disable comment after /, and a plain a / b that must keep being
densed (so the fix does not disable dense operators wholesale). The first two fail on
master.

pnpm test   →  Test Suites: 27 passed, 27 total
               Tests:       5849 passed, 1 skipped, 5850 total
pnpm run lint / pretty:check / ts:check / build   →  all clean

Regression check

master versus the fix over 12,605 combinations (6 dialects × 3 option sets × ~700 SQL
literals harvested from the test suite), with word-multiset preservation against the
input as the criterion: 7,688 byte-identical, 0 regressions. Only Snowflake
changes, and only where a / meets a comment.

…a line comment

Layout.add() had a guard that kept a layout item starting with "-" from being
glued onto one ending with "-", because that forms "--", which re-parses as a
line comment and swallows the rest of the line. The same hazard exists for any
other line comment marker a dialect uses, and Snowflake is the only dialect
where "//" starts a line comment:

    SELECT a / /* c */ b FROM t;      with denseOperators

came out as

    SELECT
      a//* c */ b
    FROM
      t;

so "b" became comment text and the statement quietly changed meaning. It also
swallowed a following /* sql-formatter-disable */ marker.

The guard now works off the dialect's own lineCommentTypes instead of a
hard-coded "--", following the same path already used for identifierDashes.
Behavior for "--" is unchanged, and dialects that do not use "//" are
unaffected.

This branch has not been deployed

No deployments
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.

1 participant