From 021dcc5103b7abb516bbb0da35b58f0636aa757f Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 23 Dec 2023 19:25:19 +0000 Subject: [PATCH] Add triggerUnit2 for trigger parsing fixes --- antlr/ApexParser.g4 | 23 ++++++ .../nawforce/apexparser/ApexTrigger2Test.java | 77 +++++++++++++++++++ npm/src/__tests__/ApexTrigger2Test.ts | 66 ++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 jvm/src/test/java/com/nawforce/apexparser/ApexTrigger2Test.java create mode 100644 npm/src/__tests__/ApexTrigger2Test.ts diff --git a/antlr/ApexParser.g4 b/antlr/ApexParser.g4 index d8c7627..73b4427 100644 --- a/antlr/ApexParser.g4 +++ b/antlr/ApexParser.g4 @@ -50,10 +50,24 @@ triggerUnit : TRIGGER id ON id LPAREN triggerCase (COMMA triggerCase)* RPAREN block EOF ; +// v2 entry point for Apex trigger files, see README.md for details +triggerUnit2 + : TRIGGER id ON id LPAREN triggerCase (COMMA triggerCase)* RPAREN triggerBlock EOF + ; + triggerCase : (BEFORE|AFTER) (INSERT|UPDATE|DELETE|UNDELETE) ; +triggerBlock + : LBRACE triggerBlockMember* RBRACE + ; + +triggerBlockMember + : modifier* triggerMemberDeclaration + | statement + ; + // entry point for Apex class files compilationUnit : typeDeclaration EOF @@ -133,6 +147,15 @@ memberDeclaration | propertyDeclaration ; +triggerMemberDeclaration + : methodDeclaration + | fieldDeclaration + | interfaceDeclaration + | classDeclaration + | enumDeclaration + | propertyDeclaration + ; + /* We use rule this even for void methods which cannot have [] after parameters. This simplifies grammar and we can consider void to be a type, which renders the [] matching as a context-sensitive issue or a semantic check diff --git a/jvm/src/test/java/com/nawforce/apexparser/ApexTrigger2Test.java b/jvm/src/test/java/com/nawforce/apexparser/ApexTrigger2Test.java new file mode 100644 index 0000000..77b0780 --- /dev/null +++ b/jvm/src/test/java/com/nawforce/apexparser/ApexTrigger2Test.java @@ -0,0 +1,77 @@ +package com.nawforce.apexparser; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.nawforce.apexparser.SyntaxErrorCounter.createParser; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ApexTrigger2Test { + + @Test + void testEmptyTrigger() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithStatement() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {System.debug('');}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithMethod() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {public void func() {}}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithField() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {String a;}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithInterface() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {interface Foo {}}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithClass() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {class Foo {}}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithEnum() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) {enum Foo {}}"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testTriggerWithProperty() { + Map.Entry parserAndCounter = createParser("trigger test on Account (before update, after update) { String a {get { return a; } set { a = value; }} }"); + ApexParser.TriggerUnit2Context context = parserAndCounter.getKey().triggerUnit2(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + +} diff --git a/npm/src/__tests__/ApexTrigger2Test.ts b/npm/src/__tests__/ApexTrigger2Test.ts new file mode 100644 index 0000000..7fb45a4 --- /dev/null +++ b/npm/src/__tests__/ApexTrigger2Test.ts @@ -0,0 +1,66 @@ +import {TriggerUnit2Context} from "../ApexParser"; +import { createParser } from "./SyntaxErrorCounter"; + +test('Empty Trigger', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with statement', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {System.debug('');}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with method', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {public void func() {}}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with field', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {String a;}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with interface', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {interface Foo {}}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with class', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {class Foo {}}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with enum', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) {enum Foo {}}") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) + +test('Trigger with property', () => { + const [parser, errorCounter] = createParser("test.trigger", "trigger test on Account (before update, after update) { String a {get { return a; } set { a = value; }} }") + const context = parser.triggerUnit2() + + expect(context).toBeInstanceOf(TriggerUnit2Context) + expect(errorCounter.getNumErrors()).toEqual(0) +}) \ No newline at end of file