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#"));
+ }
+ }
}