Skip to content

Conversation

Copy link

Copilot AI commented Dec 22, 2025

CSS parser error messages lacked context about where errors occurred and what was expected, making debugging difficult. Errors like "Error in declaration." provided no actionable information.

Changes

Core Implementation

  • ParserContext class: Stack-based context tracker maintaining rule hierarchy, current token, expected tokens, and property names
  • AbstractCSSParser: Integrated ParserContext into toCSSParseException() to automatically enrich all error messages with contextual information
  • CSS3Parser.jj grammar: Added context tracking to styleRule, declaration, and mediaRule productions using try-finally blocks

Error Message Enhancement

Before:

Error in declaration.

After:

Error in declaration. (Invalid token "blue". Was expecting one of: <S>, ":".) 
(in style-rule > declaration). Expected: property-name, : (property: background)

Nested context:

Error in declaration. (Invalid token "red". Was expecting one of: <S>, ":".) 
(in @media > style-rule > declaration). Expected: property-name, : (property: color)

Testing

  • Added ContextAwareErrorTest with 7 tests covering declaration errors, nested rules, property context, and expected tokens
  • Updated existing test expectations across 8 test files to match new error format

All 646 tests pass. Changes are backward compatible - existing error handling code unchanged.

Original prompt

Add Context-Aware Error Messages to CSS Parser

Problem

The current CSS parser produces generic error messages that lack context, making it difficult for users to understand:

  • Where in the CSS structure the error occurred
  • What was being parsed when the error happened
  • What tokens were expected vs. what was found

Current generic error messages:

Error in declaration.
Error in selector.
Invalid token "colr".

These messages don't provide:

  • The parsing context (e.g., "in @media rule > .selector")
  • The actual vs. expected tokens
  • Helpful suggestions for common mistakes
  • The full parsing stack trace

Solution

Implement a comprehensive context-aware error messaging system that provides rich, actionable error information.

1. Create ParserContext Class

package org.htmlunit.cssparser.parser;

import java.util.*;

/**
 * Tracks parsing context to provide detailed error messages.
 */
public class ParserContext {
    private final Deque<String> ruleStack = new ArrayDeque<>();
    private Token currentToken;
    private final List<String> expectedTokens = new ArrayList<>();
    private String currentProperty;
    
    public void enterRule(String ruleName) {
        ruleStack.push(ruleName);
    }
    
    public void exitRule() {
        if (!ruleStack.isEmpty()) {
            ruleStack.pop();
        }
    }
    
    public void setCurrentToken(Token token) {
        this.currentToken = token;
    }
    
    public void addExpectedToken(String token) {
        expectedTokens.add(token);
    }
    
    public void clearExpectedTokens() {
        expectedTokens.clear();
    }
    
    public void setCurrentProperty(String property) {
        this.currentProperty = property;
    }
    
    public String buildContextualMessage(String baseMessage) {
        StringBuilder sb = new StringBuilder(baseMessage);
        
        // Add rule context
        if (!ruleStack.isEmpty()) {
            sb.append(" (in ");
            sb.append(String.join(" > ", ruleStack));
            sb.append(")");
        }
        
        // Add current token info
        if (currentToken != null) {
            sb.append(" at '");
            sb.append(currentToken.image);
            sb.append("'");
        }
        
        // Add expected tokens
        if (!expectedTokens.isEmpty()) {
            sb.append(". Expected: ");
            sb.append(String.join(", ", expectedTokens));
        }
        
        // Add property context if available
        if (currentProperty != null) {
            sb.append(" (property: ");
            sb.append(currentProperty);
            sb.append(")");
        }
        
        return sb.toString();
    }
    
    public String getCurrentContext() {
        return ruleStack.isEmpty() ? "root" : String.join(" > ", ruleStack);
    }
}

2. Enhance AbstractCSSParser

// Add to AbstractCSSParser.java
private ParserContext parserContext_ = new ParserContext();

protected ParserContext getParserContext() {
    return parserContext_;
}

// Update toCSSParseException methods to use context
protected CSSParseException toCSSParseException(String messageKey, ParseException e) {
    String baseMessage = getParserMessage(messageKey);
    String contextualMessage = parserContext_.buildContextualMessage(baseMessage);
    
    if (e.currentToken != null && e.currentToken.next != null) {
        return new CSSParseException(
            contextualMessage,
            getInputSource().getURI(),
            e.currentToken.next.beginLine,
            e.currentToken.next.beginColumn
        );
    }
    return new CSSParseException(contextualMessage, getInputSource().getURI(), 1, 1);
}

3. Add Token Category Helper

// Add to AbstractCSSParser.java
protected String getTokenCategory(int kind) {
    switch(kind) {
        case CSS3ParserConstants.IDENT:
        case CSS3ParserConstants.NUMBER:
        case CSS3ParserConstants.STRING:
            return "value";
        case CSS3ParserConstants.LBRACE:
        case CSS3ParserConstants.RBRACE:
        case CSS3ParserConstants.LROUND:
        case CSS3ParserConstants.RROUND:
            return "delimiter";
        case CSS3ParserConstants.SEMICOLON:
        case CSS3ParserConstants.COLON:
        case CSS3ParserConstants.COMMA:
            return "separator";
        case CSS3ParserConstants.IMPORT_SYM:
        case CSS3ParserConstants.MEDIA_SYM:
        case CSS3ParserConstants.PAGE_SYM:
            return "at-rule";
        default:
            return "token";
    }
}

protected List<String> getExpectedTokenNames(ParseException e) {
    List<String> expected = new ArrayList<>();
    if (e.expectedTokenSequences != null) {
        for (int[] sequence : e.expectedTokenSequences) {
            if (sequence.length > 0) {
                expected.add(tokenImage[sequence[0]]);
            }
        }
    }
    return expected;
}

4. Update CSS3Parser.jj Grammar

Add context tracking to major...

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits December 22, 2025 13:22
…dates

Co-authored-by: rbri <2544132+rbri@users.noreply.github.com>
Co-authored-by: rbri <2544132+rbri@users.noreply.github.com>
Copilot AI changed the title [WIP] Add context-aware error messages to CSS parser Add context-aware error messages to CSS parser Dec 22, 2025
Copilot AI requested a review from rbri December 22, 2025 13:38
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.

2 participants