diff --git a/.all-contributorsrc b/.all-contributorsrc index 78311a6858..87892e6d84 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1053,7 +1053,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/86377278?v=4", "profile": "https://github.com/aaronhurst-google", "contributions": [ - "bug" + "bug", + "code" ] }, { diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 967675f0d1..db79a432d7 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -738,7 +738,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Yurii Dubinka

🐛
Zoltan Farkas

🐛
Zustin

🐛 -
aaronhurst-google

🐛 +
aaronhurst-google

🐛 💻
alexmodis

🐛
andreoss

🐛 diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 5e50bcf8c9..7fcbc0c973 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -40,6 +40,7 @@ This is a {{ site.pmd.release_type }} release. * [#4081](https://github.com/pmd/pmd/pull/4081): \[apex] Remove Jorje leaks outside `ast` package - [@eklimo](https://github.com/eklimo) * [#4083](https://github.com/pmd/pmd/pull/4083): \[java] UnnecessaryImport false positive for on-demand imports of nested classes (fix for #4082) - [@abyss638](https://github.com/abyss638) +* [#4092](https://github.com/pmd/pmd/pull/4092): \[apex] Implement ApexQualifiableNode for ASTUserEnum - [@aaronhurst-google](https://github.com/aaronhurst-google) {% endtocmaker %} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java index 4aef1641cc..b4db014ea9 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java @@ -8,7 +8,9 @@ import net.sourceforge.pmd.annotation.InternalApi; import apex.jorje.semantic.ast.compilation.UserEnum; -public class ASTUserEnum extends ApexRootNode { +public class ASTUserEnum extends ApexRootNode implements ApexQualifiableNode { + + private ApexQualifiedName qname; @Deprecated @InternalApi @@ -30,4 +32,20 @@ public class ASTUserEnum extends ApexRootNode { public ASTModifierNode getModifiers() { return getFirstChildOfType(ASTModifierNode.class); } + + @Override + public ApexQualifiedName getQualifiedName() { + if (qname == null) { + + ASTUserClass parent = this.getFirstParentOfType(ASTUserClass.class); + + if (parent != null) { + qname = ApexQualifiedName.ofNestedEnum(parent.getQualifiedName(), this); + } else { + qname = ApexQualifiedName.ofOuterEnum(this); + } + } + + return qname; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java index d3898a29fb..b5ce290e4a 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java @@ -148,6 +148,20 @@ public final class ApexQualifiedName implements QualifiedName { } + static ApexQualifiedName ofOuterEnum(ASTUserEnum astUserEnum) { + String ns = astUserEnum.getNamespace(); + String[] classes = {astUserEnum.getImage()}; + return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, classes, null); + } + + + static ApexQualifiedName ofNestedEnum(ApexQualifiedName parent, ASTUserEnum astUserEnum) { + String[] classes = Arrays.copyOf(parent.classes, parent.classes.length + 1); + classes[classes.length - 1] = astUserEnum.getImage(); + return new ApexQualifiedName(parent.nameSpace, classes, null); + } + + private static String getOperationString(ASTMethod node) { StringBuilder sb = new StringBuilder(); sb.append(node.getImage()).append('('); @@ -171,18 +185,28 @@ public final class ApexQualifiedName implements QualifiedName { static ApexQualifiedName ofMethod(ASTMethod node) { - ASTUserClassOrInterface parent = node.getFirstParentOfType(ASTUserClassOrInterface.class); - if (parent == null) { - ASTUserTrigger trigger = node.getFirstParentOfType(ASTUserTrigger.class); - String ns = trigger.getNamespace(); - String targetObj = trigger.getTargetName(); - - return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, new String[]{"trigger", targetObj}, trigger.getImage()); // uses a reserved word as a class name to prevent clashes - - } else { - ApexQualifiedName baseName = parent.getQualifiedName(); + // Check first, as enum must be innermost potential parent + ASTUserEnum enumParent = node.getFirstParentOfType(ASTUserEnum.class); + if (enumParent != null) { + ApexQualifiedName baseName = enumParent.getQualifiedName(); return new ApexQualifiedName(baseName.nameSpace, baseName.classes, getOperationString(node)); } + + ASTUserClassOrInterface classParent = node.getFirstParentOfType(ASTUserClassOrInterface.class); + if (classParent != null) { + ApexQualifiedName baseName = classParent.getQualifiedName(); + + return new ApexQualifiedName(baseName.nameSpace, baseName.classes, getOperationString(node)); + } + + ASTUserTrigger triggerParent = node.getFirstParentOfType(ASTUserTrigger.class); + if (triggerParent != null) { + String ns = triggerParent.getNamespace(); + String targetObj = triggerParent.getTargetName(); + + return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, new String[]{"trigger", targetObj}, triggerParent.getImage()); // uses a reserved word as a class name to prevent clashes + } + throw new UnsupportedOperationException(); } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java index cf55cb8cc5..e7be1eee33 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.util.List; @@ -94,4 +95,32 @@ public class ApexQualifiedNameTest extends ApexParserTestBase { assertEquals("c__trigger.Account#myAccountTrigger", m.getQualifiedName().toString()); } } + + + @Test + public void testUnqualifiedEnum() { + ApexNode root = parse("public enum primaryColor { RED, YELLOW, BLUE }"); + + ApexQualifiedName enumQName = ASTUserEnum.class.cast(root).getQualifiedName(); + List methods = root.findDescendantsOfType(ASTMethod.class); + + assertEquals("c__primaryColor", enumQName.toString()); + for (ASTMethod m : methods) { + assertTrue(m.getQualifiedName().toString().startsWith("c__primaryColor#")); + } + } + + @Test + public void testQualifiedEnum() { + ApexNode root = parse("public class Outer { public enum Inner { OK } }"); + + ASTUserEnum enumNode = root.getFirstDescendantOfType(ASTUserEnum.class); + ApexQualifiedName enumQName = enumNode.getQualifiedName(); + List methods = enumNode.findDescendantsOfType(ASTMethod.class); + + assertEquals("c__Outer.Inner", enumQName.toString()); + for (ASTMethod m : methods) { + assertTrue(m.getQualifiedName().toString().startsWith("c__Outer.Inner#")); + } + } }