swc-project/swc · error · swc_css_parser::error::Error

Expected id, class, attribute or pseudo-class selector

Error message

Expected id, class, attribute or pseudo-class selector

What it means

A subclass selector is one of exactly four things: '#id', '.class', '[attr]', or ':pseudo-class'. The parser was asked to produce a SubclassSelector but the current token is none of '#', '.', '[', ':'. In the compound-selector loop this position is pre-checked, so seeing this error usually means a code path (pseudo-class arguments, re-parse of fragments, direct use of the Parse impl) reached a subclass position with an unexpected token.

Source

Thrown at crates/swc_css_parser/src/parser/selectors/mod.rs:611

            value,
        })
    }
}

impl<I> Parse<SubclassSelector> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<SubclassSelector> {
        match cur!(self) {
            tok!("#") => Ok(SubclassSelector::Id(self.parse()?)),
            tok!(".") => Ok(SubclassSelector::Class(self.parse()?)),
            tok!("[") => Ok(SubclassSelector::Attribute(self.parse()?)),
            tok!(":") => Ok(SubclassSelector::PseudoClass(self.parse()?)),
            _ => {
                let span = self.input.cur_span();

                return Err(Error::new(
                    span,
                    ErrorKind::Expected("id, class, attribute or pseudo-class selector"),
                ));
            }
        }
    }
}

impl<I> Parse<IdSelector> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<IdSelector> {
        let span = self.input.cur_span();
        let text = match bump!(self) {
            Token::Hash {
                is_id, value, raw, ..
            } => {

View on GitHub (pinned to 5176682b65)

Solutions

  1. If you maintain calling code: re-check token cursor positioning before invoking subclass parsing; ensure is!('#'/"."/'['/':') guards wrap the call
  2. If hand CSS triggered it: look for a malformed pseudo-class or stray punctuation at the reported span and normalize the selector
  3. Prefer parsing whole selectors rather than re-parsed fragments where possible

Example fix

/* before (fragment re-parse misuse) */
parse_subclass("> a")
/* after */
parse_selector("> a")  // parse at the right grammar level, or guard on the first token
Defensive patterns

Strategy: type-guard

Validate before calling

// Only attempt subclass parsing when the token is one of the four openers
const subclassOpeners = new Set(['#', '.', '[', ':']);
function isSubclassStart(tok) { return subclassOpeners.has(tok); }

Type guard

// Rust
fn is_subclass_start(t: &Token) -> bool {
  matches!(t, Token::Hash { .. } | Token::Delim { value: '.' | '[' | ':' | '#', .. })
}

Try / catch

// Rust: guard the call instead of relying on the error
if is_subclass_start(&cur) { let sel: SubclassSelector = parser.parse()?; } else { /* different grammar production */ }

Prevention

When it happens

Trigger: Selector fragments re-parsed against subclass grammar where the token is a combinator/delimiter, e.g. tooling that calls Parse::<SubclassSelector> on '> a', or malformed pseudo-class argument lists that route into subclass parsing with the cursor on the wrong token.

Common situations: Tools built on swc_css_parser that slice and re-parse selector fragments (linters, CSS modules, minifiers) with an off-by-one token position; rarely hand-authored CSS, since the guarded loop normally reports the friendlier InvalidSelector instead.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/d0ed95d8471114de. Report an issue: GitHub.