From 5e8e63d7372e8b5aeb2c85ad9cab00e93a77d184 Mon Sep 17 00:00:00 2001 From: Rick Roesler Date: Mon, 27 Jul 2026 12:09:32 -0700 Subject: [PATCH] Fix dataCategoryName: close parenthesized data category list with RPAREN The second alternative of the dataCategoryName rule ended with LPAREN instead of RPAREN, so valid multi-category filters such as WITH DATA CATEGORY Geography__c ABOVE_OR_BELOW (usa__c, uk__c) failed to parse. Adds regression tests to both npm and jvm targets and a changelog entry. Implemented by codex (gpt-5.6-sol); committed by orchestrator on its behalf. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 4 +++ antlr/BaseApexParser.g4 | 2 +- .../apexparser/SOQLParserTest.java | 30 +++++++++++++++++++ npm/test/SOQLParserTest.ts | 30 +++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31b1e43..7ba3f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # apex-parser - Changelog +## Unreleased + +- Fix the `dataCategoryName` grammar rule so parenthesized SOQL data category lists close with `RPAREN`; previously, valid multi-category `WITH DATA CATEGORY` filters failed to parse. + ## 5.1.0 - 2026-07-03 - Allow functions in `GROUP BY` clause of SOQL queries diff --git a/antlr/BaseApexParser.g4 b/antlr/BaseApexParser.g4 index 03bc5d2..44ec10b 100644 --- a/antlr/BaseApexParser.g4 +++ b/antlr/BaseApexParser.g4 @@ -762,7 +762,7 @@ dataCategorySelection dataCategoryName : soqlId - | LPAREN soqlId (COMMA soqlId)* LPAREN; + | LPAREN soqlId (COMMA soqlId)* RPAREN; filteringSelector : AT | ABOVE | BELOW | ABOVE_OR_BELOW; diff --git a/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java b/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java index efbcb8e..3f8727c 100644 --- a/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java +++ b/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java @@ -43,6 +43,36 @@ void testSOQL() { assertEquals(0, parserAndCounter.getValue().getNumErrors()); } + @Test + void testSOQLWhereWithSingleDataCategoryFilter() { + Map.Entry parserAndCounter = createParser( + "SELECT Title FROM KnowledgeArticleVersion WHERE PublishStatus='online' WITH DATA CATEGORY Geography__c ABOVE usa__c" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testSOQLWhereWithDataCategoryListFilter() { + Map.Entry parserAndCounter = createParser( + "SELECT Title FROM Question WHERE LastReplyDate > 2005-10-08T01:02:03Z WITH DATA CATEGORY Geography__c AT (usa__c, uk__c)" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testSOQLWhereWithMultipleDataCategoryFilters() { + Map.Entry parserAndCounter = createParser( + "SELECT UrlName FROM KnowledgeArticleVersion WHERE PublishStatus='draft' WITH DATA CATEGORY Geography__c AT usa__c AND Product__c ABOVE_OR_BELOW mobile_phones__c" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + @Test void testCurrencyLiteral() { Map.Entry parserAndCounter = createParser( diff --git a/npm/test/SOQLParserTest.ts b/npm/test/SOQLParserTest.ts index 3b89fbb..7d8416b 100644 --- a/npm/test/SOQLParserTest.ts +++ b/npm/test/SOQLParserTest.ts @@ -26,6 +26,36 @@ test("SOQL Query", () => { expect(errorCounter.getNumErrors()).toEqual(0); }); +test("SOQL Where with single data category filter", () => { + const [parser, errorCounter] = createParser( + "SELECT Title FROM KnowledgeArticleVersion WHERE PublishStatus='online' WITH DATA CATEGORY Geography__c ABOVE usa__c" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + +test("SOQL Where with data category list filter", () => { + const [parser, errorCounter] = createParser( + "SELECT Title FROM Question WHERE LastReplyDate > 2005-10-08T01:02:03Z WITH DATA CATEGORY Geography__c AT (usa__c, uk__c)" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + +test("SOQL Where with multiple data category filters", () => { + const [parser, errorCounter] = createParser( + "SELECT UrlName FROM KnowledgeArticleVersion WHERE PublishStatus='draft' WITH DATA CATEGORY Geography__c AT usa__c AND Product__c ABOVE_OR_BELOW mobile_phones__c" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + test("SOQL Query Using Field function", () => { const [parser, errorCounter] = createParser( "Select Fields(All) from Account"