From e2b1f0f3b9c9b4a052cc9baebc45c8ad13a2d1d3 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 1 Jul 2021 17:01:48 +0200 Subject: [PATCH] [java] Add experimental ASTGuardedPattern Now we can parse guarded patterns in switch labels. --- pmd-java/etc/grammar/Java.jjt | 30 ++- .../pmd/lang/java/ast/ASTGuardedPattern.java | 38 ++++ .../java/ast/JavaParserDecoratedVisitor.java | 9 + .../java/ast/JavaParserVisitorAdapter.java | 8 + .../java/ast/JavaParserVisitorDecorator.java | 7 + .../pmd/lang/java/rule/AbstractJavaRule.java | 8 + .../java/ast/Java17PreviewTreeDumpTest.java | 2 - .../GuardedAndParenthesizedPatterns.txt | 189 ++++++++++++++++++ 8 files changed, 287 insertions(+), 4 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 6b1717c27e..fd38f40fc3 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -550,6 +550,12 @@ public class JavaParser { } } + private void checkForGuardedPatterns() { + if (!isJEP406Supported()) { + throwParseException("Guarded patterns are only supported with JDK 17 Preview."); + } + } + // This is a semantic LOOKAHEAD to determine if we're dealing with an assert // Note that this can't be replaced with a syntactic lookahead // since "assert" isn't a string literal token @@ -1748,6 +1754,25 @@ void EqualityExpression() #EqualityExpression(>1): InstanceOfExpression() ( LOOKAHEAD(2) ( "==" {jjtThis.setImage("==");} | "!=" {jjtThis.setImage("!=");} ) InstanceOfExpression() )* } +void Pattern() #void: +{} +{ + PrimaryPattern() [ GuardedPatternCondition() #GuardedPattern(2) {checkForGuardedPatterns();} ] +} + +void GuardedPatternCondition() #void: +{} +{ + "&&" ConditionalAndExpression() +} + +void PrimaryPattern() #void: +{} +{ + TypePattern() + | "(" Pattern() ")" +} + void TypePattern(): {} { @@ -2242,11 +2267,12 @@ void SwitchLabel() : void CaseLabelElement() #void: {} { - NullLiteral() {checkForNullCaseLabel();} + LOOKAHEAD(1) NullLiteral() {checkForNullCaseLabel();} | "default" {checkForDefaultCaseLabel();} | - LOOKAHEAD(Type() ) Type() VariableDeclaratorId() #TypePattern(2) {checkForPatternMatchingInSwitch();} + // LOOKAHEAD(Type() ) Type() VariableDeclaratorId() #TypePattern(2) {checkForPatternMatchingInSwitch();} + LOOKAHEAD(Pattern()) Pattern() {checkForPatternMatchingInSwitch();} | ConditionalExpression() #Expression } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java new file mode 100644 index 0000000000..91c220e613 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java @@ -0,0 +1,38 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import net.sourceforge.pmd.annotation.Experimental; + +/** + * A guarded pattern (JDK17 Preview). This can be found + * in {@link ASTSwitchLabel}s. + * + *
+ *
+ * GuardedPattern ::= {@linkplain ASTPattern} "&&" {@linkplain ASTConditionalAndExpression}
+ *
+ * 
+ * + * @see JEP 406: Pattern Matching for switch (Preview) +*/ +@Experimental +public final class ASTGuardedPattern extends AbstractJavaNode implements ASTPattern { + + ASTGuardedPattern(int id) { + super(id); + } + + ASTGuardedPattern(JavaParser p, int id) { + super(p, id); + } + + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java index b5f224453e..00cf32dd3a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.annotation.Experimental; + /** * External wrapper for a visitor decorator. This one drives the AST visit, delegating to the base controlless visitor * given in the constructor. Add decorators using the {@link #decorateWith(JavaParserVisitorDecorator)}. @@ -940,4 +942,11 @@ public class JavaParserDecoratedVisitor implements JavaParserVisitor { visitor.visit(node, data); return visit((JavaNode) node, data); } + + @Experimental + @Override + public Object visit(ASTGuardedPattern node, Object data) { + visitor.visit(node, data); + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java index d3a8410d05..5b1205cd46 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.annotation.Experimental; + public class JavaParserVisitorAdapter implements JavaParserVisitor { @Override @@ -660,4 +662,10 @@ public class JavaParserVisitorAdapter implements JavaParserVisitor { public Object visit(ASTPermitsList node, Object data) { return visit((JavaNode) node, data); } + + @Experimental + @Override + public Object visit(ASTGuardedPattern node, Object data) { + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java index bfa64fdfe5..03f8eb703a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.annotation.InternalApi; /** @@ -793,4 +794,10 @@ public class JavaParserVisitorDecorator implements JavaParserControllessVisitor public Object visit(ASTPermitsList node, Object data) { return visitor.visit(node, data); } + + @Experimental + @Override + public Object visit(ASTGuardedPattern node, Object data) { + return visitor.visit(node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index 04aefa3f0b..0a92976605 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.rule; import java.util.List; import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.JavaLanguageModule; @@ -59,6 +60,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTForStatement; import net.sourceforge.pmd.lang.java.ast.ASTForUpdate; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; +import net.sourceforge.pmd.lang.java.ast.ASTGuardedPattern; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; @@ -870,4 +872,10 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse public Object visit(ASTPermitsList node, Object data) { return visit((JavaNode) node, data); } + + @Experimental + @Override + public Object visit(ASTGuardedPattern node, Object data) { + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java index 354c8cfb62..9956a87970 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.lang.java.ast; -import org.junit.Ignore; import org.junit.Test; import net.sourceforge.pmd.lang.ast.ParseException; @@ -59,7 +58,6 @@ public class Java17PreviewTreeDumpTest extends BaseTreeDumpTest { } @Test - @Ignore("not finished yet") public void guardedAndParenthesizedPatterns() { doTest("GuardedAndParenthesizedPatterns"); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt new file mode 100644 index 0000000000..bc0012a0c5 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt @@ -0,0 +1,189 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = true] + +- TypeDeclaration[] + +- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "GuardedAndParenthesizedPatterns", @Default = false, @Final = false, @Image = "GuardedAndParenthesizedPatterns", @Interface = false, @Local = false, @Modifiers = 1, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = false, @SimpleName = "GuardedAndParenthesizedPatterns", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false] + +- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false] + +- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD] + | +- MethodDeclaration[@Abstract = false, @Arity = 1, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "test", @Modifiers = 16, @Name = "test", @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = true, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = false, @Transient = false, @Void = true, @Volatile = false] + | +- ResultType[@Void = true, @returnsArray = false] + | +- MethodDeclarator[@Image = "test", @ParameterCount = 1] + | | +- FormalParameters[@ParameterCount = 1, @Size = 1] + | | +- FormalParameter[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @ExplicitReceiverParameter = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @Varargs = false, @Volatile = false] + | | +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "Object"] + | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Object", @ReferenceToClassSameCompilationUnit = false] + | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @ForeachVariable = false, @FormalParameter = true, @Image = "o", @LambdaParameter = false, @LocalVariable = false, @Name = "o", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "o"] + | +- Block[@containsComment = false] + | +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- SwitchStatement[@DefaultCase = false, @ExhaustiveEnumSwitch = false] + | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "o"] + | +- SwitchLabeledExpression[] + | | +- SwitchLabel[@Default = false] + | | | +- GuardedPattern[] + | | | +- TypePattern[] + | | | | +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "String"] + | | | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "String", @ReferenceToClassSameCompilationUnit = false] + | | | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @ForeachVariable = false, @FormalParameter = false, @Image = "s", @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "s"] + | | | +- PrimaryExpression[] + | | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Expression[@StandAlonePrimitive = false] + | | | +- EqualityExpression[@Image = "==", @Operator = "=="] + | | | +- PrimaryExpression[] + | | | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | | | +- Name[@Image = "s.length"] + | | | | +- PrimarySuffix[@ArgumentCount = 0, @Arguments = true, @ArrayDereference = false] + | | | | +- Arguments[@ArgumentCount = 0, @Size = 0] + | | | +- PrimaryExpression[] + | | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1", @FloatLiteral = false, @Image = "1", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Name[@Image = "System.out.println"] + | | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | | +- Arguments[@ArgumentCount = 1, @Size = 1] + | | +- ArgumentList[@Size = 1] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""single char string"", @FloatLiteral = false, @Image = ""single char string"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""single char string"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + | +- SwitchLabeledExpression[] + | | +- SwitchLabel[@Default = false] + | | | +- TypePattern[] + | | | +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "String"] + | | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "String", @ReferenceToClassSameCompilationUnit = false] + | | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @ForeachVariable = false, @FormalParameter = false, @Image = "s", @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "s"] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Name[@Image = "System.out.println"] + | | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | | +- Arguments[@ArgumentCount = 1, @Size = 1] + | | +- ArgumentList[@Size = 1] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""string"", @FloatLiteral = false, @Image = ""string"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""string"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + | +- SwitchLabeledExpression[] + | | +- SwitchLabel[@Default = false] + | | | +- GuardedPattern[] + | | | +- TypePattern[] + | | | | +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "Integer"] + | | | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Integer", @ReferenceToClassSameCompilationUnit = false] + | | | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @ForeachVariable = false, @FormalParameter = false, @Image = "i", @LambdaParameter = false, @LocalVariable = false, @Name = "i", @PatternBinding = true, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "i"] + | | | +- EqualityExpression[@Image = "==", @Operator = "=="] + | | | +- PrimaryExpression[] + | | | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | | | +- Name[@Image = "i.intValue"] + | | | | +- PrimarySuffix[@ArgumentCount = 0, @Arguments = true, @ArrayDereference = false] + | | | | +- Arguments[@ArgumentCount = 0, @Size = 0] + | | | +- PrimaryExpression[] + | | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1", @FloatLiteral = false, @Image = "1", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | | +- Name[@Image = "System.out.println"] + | | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | | +- Arguments[@ArgumentCount = 1, @Size = 1] + | | +- ArgumentList[@Size = 1] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""integer 1"", @FloatLiteral = false, @Image = ""integer 1"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""integer 1"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + | +- SwitchLabeledExpression[] + | +- SwitchLabel[@Default = true] + | +- Expression[@StandAlonePrimitive = false] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "System.out.println"] + | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 1, @Size = 1] + | +- ArgumentList[@Size = 1] + | +- Expression[@StandAlonePrimitive = false] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""default case"", @FloatLiteral = false, @Image = ""default case"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""default case"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + +- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD] + +- MethodDeclaration[@Abstract = false, @Arity = 1, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "main", @Modifiers = 17, @Name = "main", @Native = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Static = true, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = true, @Transient = false, @Void = true, @Volatile = false] + +- ResultType[@Void = true, @returnsArray = false] + +- MethodDeclarator[@Image = "main", @ParameterCount = 1] + | +- FormalParameters[@ParameterCount = 1, @Size = 1] + | +- FormalParameter[@Abstract = false, @Array = true, @ArrayDepth = 1, @Default = false, @ExplicitReceiverParameter = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @Varargs = false, @Volatile = false] + | +- Type[@Array = true, @ArrayDepth = 1, @ArrayType = true, @TypeImage = "String"] + | | +- ReferenceType[@Array = true, @ArrayDepth = 1] + | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = true, @ArrayDepth = 1, @Image = "String", @ReferenceToClassSameCompilationUnit = false] + | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = true, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @ForeachVariable = false, @FormalParameter = true, @Image = "args", @LambdaParameter = false, @LocalVariable = false, @Name = "args", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "args"] + +- Block[@containsComment = false] + +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- StatementExpression[] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "test"] + | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 1, @Size = 1] + | +- ArgumentList[@Size = 1] + | +- Expression[@StandAlonePrimitive = false] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""a"", @FloatLiteral = false, @Image = ""a"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = true, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""a"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- StatementExpression[] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "test"] + | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 1, @Size = 1] + | +- ArgumentList[@Size = 1] + | +- Expression[@StandAlonePrimitive = false] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""fooo"", @FloatLiteral = false, @Image = ""fooo"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""fooo"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- StatementExpression[] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "test"] + | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 1, @Size = 1] + | +- ArgumentList[@Size = 1] + | +- Expression[@StandAlonePrimitive = true] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1", @FloatLiteral = false, @Image = "1", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1] + +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- StatementExpression[] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "test"] + | +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 1, @Size = 1] + | +- ArgumentList[@Size = 1] + | +- Expression[@StandAlonePrimitive = true] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1L", @FloatLiteral = false, @Image = "1L", @IntLiteral = false, @LongLiteral = true, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1L", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1] + +- BlockStatement[@Allocation = false] + +- Statement[] + +- StatementExpression[] + +- PrimaryExpression[] + +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Name[@Image = "test"] + +- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false] + +- Arguments[@ArgumentCount = 1, @Size = 1] + +- ArgumentList[@Size = 1] + +- Expression[@StandAlonePrimitive = false] + +- PrimaryExpression[] + +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = null, @FloatLiteral = false, @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = null, @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + +- NullLiteral[]