{"record":{"id":"98d19252ae253e05","repo":"swc-project/swc","slug":"expected-function-or","errorCode":null,"errorMessage":"Expected function or '('","messagePattern":"Expected function or '\\('","errorType":"validation","errorClass":"swc_css_parser::error::Error","httpStatus":null,"severity":"error","filePath":"crates/swc_css_parser/src/parser/at_rules/mod.rs","lineNumber":1282,"sourceCode":"                                ),\n                            ));\n                        }\n                    }\n                }\n\n                if !found_ident {\n                    return Err(Error::new(\n                        block.span,\n                        ErrorKind::Expected(\"ident at first position in <general-enclosed>\"),\n                    ));\n                }\n\n                Ok(GeneralEnclosed::SimpleBlock(block))\n            }\n            _ => {\n                let span = self.input.cur_span();\n\n                Err(Error::new(span, ErrorKind::Expected(\"function or '('\")))\n            }\n        }\n    }\n}\n\nimpl<I> Parse<DocumentPreludeMatchingFunction> for Parser<I>\nwhere\n    I: ParserInput,\n{\n    fn parse(&mut self) -> PResult<DocumentPreludeMatchingFunction> {\n        match cur!(self) {\n            tok!(\"url\") => Ok(DocumentPreludeMatchingFunction::Url(self.parse()?)),\n            Token::Function {\n                value: function_name,\n                ..\n            } => {\n                if matches_eq_ignore_ascii_case!(function_name, \"url\", \"src\") {\n                    Ok(DocumentPreludeMatchingFunction::Url(self.parse()?))","sourceCodeStart":1264,"sourceCodeEnd":1300,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_css_parser/src/parser/at_rules/mod.rs#L1264-L1300","documentation":"Thrown by swc_css_parser while parsing a <general-enclosed> operand, most visibly in an @supports prelude (and in the media-query fallback chain). After a logical keyword, the grammar only accepts a function token such as selector(...), media(...) or supports(...), or a parenthesized block whose first non-whitespace component value is an identifier. When the current token is neither a function nor '(', the parser returns ErrorKind::Expected(\"function or '('\") with the span of the offending token and the at-rule prelude fails to parse.","triggerScenarios":"A supports condition continued with a non-function, non-parenthesized token: '@supports (display: grid) or 5px { }', '@supports \"foo\" { }', '@supports 7 { }', a stray hash/number after and/or/not, or a truncated prelude where the leading '(' of the next operand was dropped (common with string concatenation of conditions).","commonSituations":"Hand-written @supports rules with typos, template engines or CSS-in-JS glue emitting partial condition fragments, preprocessing steps that strip or insert tokens when rewriting supports queries, and CSS copied from specs/demos that got truncated in transit.","solutions":["Put every operand of the condition in parentheses: @supports (display: grid) and (display: flex) { }","Use the special functions for non-declaration queries: @supports selector(a > b), @supports media(...), @supports supports(...)","Delete stray tokens after and/or/not in the @supports prelude","Use the reported span: it marks exactly the token that is neither a function nor '(' — search that offset in your generated CSS"],"exampleFix":"/* before */\n@supports (display: grid) or 5px {\n  .grid { display: grid; }\n}\n\n/* after */\n@supports (display: grid) or (display: flex) {\n  .grid { display: grid; }\n}","handlingStrategy":"try-catch","validationCode":"fn supports_operands_ok(css: &str) -> bool {\n    let prelude = css.split_once('{').map_or(css, |(p, _)| p);\n    let body = prelude.splitn(2, \"supports\").nth(1).unwrap_or(\"\");\n    for tok in body.split_whitespace() {\n        let t = tok.trim_matches(|c| c == ',');\n        if t.eq_ignore_ascii_case(\"and\") || t.eq_ignore_ascii_case(\"or\") || t.eq_ignore_ascii_case(\"not\") {\n            continue;\n        }\n        if !(t.starts_with('(') || t.ends_with(')')) {\n            return false;\n        }\n    }\n    true\n}","typeGuard":"fn is_supports_operand_error(e: &swc_css_parser::error::Error) -> bool {\n    matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == \"function or '('\")\n}","tryCatchPattern":"let mut errors = Vec::new();\nmatch parse_file::<Stylesheet>(&fm, None, config, &mut errors) {\n    Ok(sheet) => handle(sheet),\n    Err(err) if is_supports_operand_error(&err) => {\n        let (span, _) = *err.into_inner();\n        reject_css_with_location(css, span, \"each @supports operand must be (...) or a function like selector(...)\");\n    }\n    Err(err) => return Err(err.into()),\n}","preventionTips":["Lint supports preludes with stylelint before passing CSS to swc","When concatenating condition fragments, always append '(...)' together with the keyword","Keep fixture tests covering each @supports form you emit","Reject user-authored at-rules that are not on an allowlist"],"tags":["css","swc","parser","at-rules","supports-rule","syntax"],"backgroundTag":"css-supports-condition-syntax","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}