Skip to content

Don't let quotes in Pug regex literals hide the end of an attribute list - #20572

Merged
RobinMalfait merged 3 commits into
mainfrom
robin/pug-regex-quotes
Oct 14, 2026
Merged

RobinMalfait merged 3 commits into
mainfrom
robin/pug-regex-quotes

Conversation

@RobinMalfait

Copy link
Copy Markdown
Contributor

This is a follow-up to #20547.

That PR tracks strings inside Pug attribute lists, so that brackets in values like a(title="a (b") are ignored. But a quote that isn't a string delimiter, like the " in a regex such as input(pattern=/"/.source), opened a string that never closed. That string hid the ) of the attribute list. The list stayed open, so dots in class shorthands on later lines were not turned into spaces, and classes like underline in span.underline.font-bold were not extracted.

input(pattern=/"/.source)
span.underline.font-bold km

Solution

Only template literals can span lines in JavaScript. So when a ' or " string reaches the end of its line without a closing quote, it was not a string. In that case, we go back to just after the quote and scan the rest of the line again, with quotes as plain text. Each line is scanned at most twice, so this stays linear.

Detecting regex literals directly needs a JavaScript tokenizer, because a / can also be a division. This approach also covers other unpaired quotes, like // don't cache in a script. block.

The regression is not released yet, so there is no changelog entry: the existing entry of #20547 covers it.

Test plan

Added cases to test_unbalanced_brackets_in_quoted_attribute_values in pug.rs. They fail without this change.

cargo test -p tailwindcss-oxide

…he line

A quote that doesn't close on its line, like the `"` in a regex such as
`input(pattern=/"/.source)`, hid the `)` of the attribute list, so the
list stayed open and class shorthands on later lines were not extracted.
Scan the rest of the line again with quotes as text instead.
@RobinMalfait
RobinMalfait requested a review from a team as a code owner October 14, 2026 15:18
@greptile-apps

greptile-apps Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] Changes how Pug template attribute parsing handles quotes.

The PR still needs the existing fix for regex quotes followed by real quoted attribute values before merging.

Reviews (3) · Last reviewed commit: "Merge branch 'main' into robin/pug-regex..." · Reviewed by Greptile

Comment on lines +70 to +71
quotes_are_text = true;
cursor.move_to(quote_start + 1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Quoted brackets still hide classes

For input(pattern=/"/.source title='a (b') followed by span.underline.font-bold km, the replay disables the real single quotes too. The ( inside title is pushed onto bracket_stack, so the attribute list's ) closes that bracket instead of the list. The next line's dots stay unchanged, and underline is missed.

Preserve real quoted values during recovery and add this combined regression case.

Comment thread crates/oxide/src/extractor/pre_processors/pug.rs Outdated
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 84b9cec3-25af-4225-ae35-ea8c71b5b317
📥 Commits

Reviewing files that changed from the base of the PR and between ebb22d2 and 751bde2.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f8a3af62-40c3-4e73-92f6-4721473bf2b9
📥 Commits

Reviewing files that changed from the base of the PR and between bd67f94 and ebb22d2.

📒 Files selected for processing: 1
  • crates/oxide/src/extractor/pre_processors/pug.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Walkthrough

Pug::process now records the opening position of quoted values. If a non-template quote remains open at a newline or the end of input, the processor resumes scanning just after that opening quote and treats quotes as text. It resets this mode at the next newline. New tests cover regex expressions containing quotes in Pug attributes, including input without a trailing newline.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to ebb22

The change makes Pug class extraction recover after unpaired quotes in attribute values, and regression tests cover it. No concrete merge-blocking risk was identified in the supplied evidence.

Pre-merge checks: Passed 4
Passed Passed checks: 4 passed
Check name Status Explanation
Description check Passed The description clearly explains the Pug parsing bug, the fix, the regression tests, and the test command.
Title check Passed The title clearly identifies the main change: preventing quotes in Pug regex literals from hiding the end of an attribute list.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Preserve the attribute stack on mismatched delimiters. · pug.rs:58-79

crates/oxide/src/extractor/pre_processors/pug.rs:58-79
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the attribute stack on mismatched delimiters.

When the rescan handles input(pattern=/[")]/.source) across a line break, the ) and ] inside the regex character class do not match the outer attribute-list delimiter. BracketStack::pop still removes entries before checking the delimiter, so the rescan closes the attribute context early. A following multiline attribute such as href="x.flex" is then processed outside the attribute list, and its dot is replaced with a space. Make mismatched pops non-mutating.

Suggested fix
         if self.bracket_stack_len == 0 {
             return false;
         }

-        self.bracket_stack_len -= 1;
         unsafe {
             if *self.bracket_stack.get_unchecked(self.bracket_stack_len) != bracket {
                 return false;
             }
         }
+        self.bracket_stack_len -= 1;

         true

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e6cbf47e-07e9-4730-ae33-63f155cef0c5
📥 Commits

Reviewing files that changed from the base of the PR and between 8208518 and bd67f94.

📒 Files selected for processing: 1
  • crates/oxide/src/extractor/pre_processors/pug.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

RobinMalfait and others added 2 commits October 14, 2026 17:49
A regex quote on the last line of a file without a trailing newline kept
its string open until the end, so the line was not scanned again and
class shorthands after the attribute list were not extracted. E.g.:
`div(data-pattern=/"/.source): span.underline.font-bold`
@RobinMalfait
RobinMalfait enabled auto-merge (squash) October 14, 2026 16:59
@RobinMalfait
RobinMalfait merged commit 2ce6652 into main Oct 14, 2026
10 checks passed
@RobinMalfait
RobinMalfait deleted the robin/pug-regex-quotes branch October 14, 2026 17:07
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