[apex] Fix parsing of method decl. inside triggers

This commit is contained in:
Andreas Dangel 2024-09-06 18:42:52 +02:00
parent 97fe106724
commit fe0d05d8d5
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
4 changed files with 65 additions and 4 deletions

View File

@ -218,7 +218,11 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua
}.apply {
buildModifiers(node.modifiers).also { it.setParent(this) }
if (node is TriggerDeclaration) {
// 1. Create a synthetic "invoke" ASTMethod for the trigger body
// 1. Add all extra methods that are defined in the trigger
node.body.filterIsInstance<MethodDeclaration>().forEach { it ->
buildMethodDeclaration(it, this)?.also { it.setParent(this) }
}
// 2. Create a synthetic "invoke" ASTMethod for the trigger body
val invokeMethod = ASTMethod(
/* name= */ "invoke",
/* internalName= */ "<invoke>",
@ -226,10 +230,11 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua
/* returnType= */ "void",
SourceLocation.UNKNOWN,
).also{ it.setParent(this) }
// 2. Add the expected ASTModifier child node
// 3. Add the expected ASTModifier child node
buildModifiers(emptyList()).also { it.setParent(invokeMethod) }
// 3. Elide the body CompoundStatement->ASTBlockStatement
node.body.forEach { buildAndSetParent(it, parent = invokeMethod as AbstractApexNode) }
// 4. Elide the remaining body CompoundStatement->ASTBlockStatement
node.body.filterNot { it is MethodDeclaration }
.forEach { buildAndSetParent(it, parent = invokeMethod as AbstractApexNode) }
} else {
buildChildren(node, parent = this, exclude = { it in node.modifiers })
}

View File

@ -86,4 +86,12 @@ class ApexTreeDumpTest extends BaseTreeDumpTest {
void groupingInSoql() {
doTest("GroupingInSoql");
}
/**
* @see <a href="https://github.com/pmd/pmd/issues/5138">[apex] Seeing false-negatives on PMD 7.3.0 that were not there with 7.2.0</a>
*/
@Test
void triggersWithMethods() {
doTest("TriggerWithMethod");
}
}

View File

@ -0,0 +1,11 @@
// based on https://github.com/SalesforceLabs/EmailResponseConsole/blob/master/triggers/HVEMApprovalProcessAction.trigger
trigger TriggerWithMethod on DraftEmailMessage__c (after update){
String body = 'email body';
for(DraftEmailMessage__c demInstance : Trigger.New){
sendEmail(demInstance, emailBody);
}
private void sendEmail(DraftEmailMessage__c demInstance, String emailBody) {
}
}

View File

@ -0,0 +1,37 @@
+- ApexFile[@DefiningType = "TriggerWithMethod", @RealLoc = true]
+- UserTrigger[@DefiningType = "TriggerWithMethod", @Image = "TriggerWithMethod", @Nested = false, @RealLoc = true, @SimpleName = "TriggerWithMethod", @TargetName = "DraftEmailMessage__c", @Usages = (TriggerUsage.AFTER_UPDATE)]
+- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Field[@DefiningType = "TriggerWithMethod", @Image = "body", @Name = "body", @RealLoc = true, @Type = "String", @Value = "email body"]
| +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Method[@Arity = 2, @CanonicalName = "sendEmail", @Constructor = false, @DefiningType = "TriggerWithMethod", @Image = "sendEmail", @RealLoc = true, @ReturnType = "void", @StaticInitializer = false]
| +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 2, @Override = false, @Private = true, @Protected = false, @Public = false, @RealLoc = true, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- Parameter[@DefiningType = "TriggerWithMethod", @Image = "demInstance", @RealLoc = true, @Type = "DraftEmailMessage__c"]
| | +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- Parameter[@DefiningType = "TriggerWithMethod", @Image = "emailBody", @RealLoc = true, @Type = "String"]
| | +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- BlockStatement[@CurlyBrace = true, @DefiningType = "TriggerWithMethod", @RealLoc = true]
+- Method[@Arity = 0, @CanonicalName = "invoke", @Constructor = false, @DefiningType = "TriggerWithMethod", @Image = "invoke", @RealLoc = false, @ReturnType = "void", @StaticInitializer = false]
+- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- FieldDeclarationStatements[@DefiningType = "TriggerWithMethod", @RealLoc = true, @TypeArguments = (), @TypeName = "String"]
| +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- FieldDeclaration[@DefiningType = "TriggerWithMethod", @Image = "body", @Name = "body", @RealLoc = true]
| +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "TriggerWithMethod", @Double = false, @Image = "email body", @Integer = false, @LiteralType = LiteralType.STRING, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = true]
| +- VariableExpression[@DefiningType = "TriggerWithMethod", @Image = "body", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- ForEachStatement[@DefiningType = "TriggerWithMethod", @RealLoc = true]
+- VariableDeclarationStatements[@DefiningType = "TriggerWithMethod", @RealLoc = true]
| +- ModifierNode[@Abstract = false, @DefiningType = "TriggerWithMethod", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- VariableDeclaration[@DefiningType = "TriggerWithMethod", @Image = "demInstance", @RealLoc = true, @Type = "DraftEmailMessage__c"]
| +- VariableExpression[@DefiningType = "TriggerWithMethod", @Image = "demInstance", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- VariableExpression[@DefiningType = "TriggerWithMethod", @Image = "demInstance", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- BlockStatement[@CurlyBrace = true, @DefiningType = "TriggerWithMethod", @RealLoc = true]
| +- ExpressionStatement[@DefiningType = "TriggerWithMethod", @RealLoc = true]
| +- MethodCallExpression[@DefiningType = "TriggerWithMethod", @FullMethodName = "sendEmail", @InputParametersSize = 2, @MethodName = "sendEmail", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- VariableExpression[@DefiningType = "TriggerWithMethod", @Image = "demInstance", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- VariableExpression[@DefiningType = "TriggerWithMethod", @Image = "emailBody", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- TriggerVariableExpression[@DefiningType = "TriggerWithMethod", @RealLoc = true]