diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt
index 165e1737ea..25bbe28f41 100644
--- a/pmd-java/etc/grammar/Java.jjt
+++ b/pmd-java/etc/grammar/Java.jjt
@@ -2392,7 +2392,7 @@ void RSIGNEDSHIFT() #void:
void Annotation():
{}
{
- "@" jjtThis.name=VoidName() [ AnnotationMemberList() ]
+ "@" ClassName() [ AnnotationMemberList() ]
}
void AnnotationMemberList():
@@ -2556,6 +2556,13 @@ String VoidName() #void:
{return s.toString();}
}
+// Produces a ClassOrInterfaceType, possibly with an ambiguous LHS
+void ClassName() #void:
+{}
+{
+ AmbiguousName() { forceTypeContext(); }
+}
+
// This is used to get JJTree to generate a node.
// Variable references are always ambiguous
// when they're parsed, so they're not created
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java
index 94719f98dc..c9d5734939 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java
@@ -9,45 +9,46 @@ import java.util.Iterator;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.ast.NodeStream;
-import net.sourceforge.pmd.util.StringUtil;
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
/**
* Represents an annotation.
*
*
*
- * Annotation ::= "@" Name {@link ASTAnnotationMemberList AnnotationMemberList}?
+ * Annotation ::= "@" {@link ASTClassOrInterfaceType ClassName} {@link ASTAnnotationMemberList AnnotationMemberList}?
*
*
*/
public final class ASTAnnotation extends AbstractJavaTypeNode implements TypeNode, ASTMemberValue, Iterable {
- String name;
-
ASTAnnotation(int id) {
super(id);
}
/**
- * Returns the name of the annotation as it is used,
- * eg {@code java.lang.Override} or {@code Override}.
+ * Returns the node that represents the name of the annotation.
*/
- public String getAnnotationName() {
- return name;
+ public ASTClassOrInterfaceType getTypeNode() {
+ return (ASTClassOrInterfaceType) getChild(0);
}
- @Override
- @Deprecated
- public String getImage() {
- return name;
+ /**
+ * Returns the symbol of the annotation type.
+ */
+ public JClassSymbol getSymbol() {
+ // This cast would fail if you use a type parameter as an
+ // annotation name
+ return (JClassSymbol) getTypeNode().getReferencedSym();
}
+
/**
* Returns the simple name of the annotation.
*/
public String getSimpleName() {
- return StringUtil.substringAfterLast(getImage(), '.');
+ return getTypeNode().getSimpleName();
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java
index d44fbd225d..9e96b9f21c 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java
@@ -9,6 +9,7 @@ import java.util.Collections;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
@@ -31,7 +32,7 @@ public final class JavaDesignerBindings extends DefaultDesignerBindings {
@Override
public Attribute getMainAttribute(Node node) {
if (node instanceof JavaNode) {
- Attribute attr = ((JavaNode) node).acceptVisitor(MainAttrVisitor.INSTANCE, null);
+ Attribute attr = node.acceptVisitor(MainAttrVisitor.INSTANCE, null);
if (attr != null) {
return attr;
}
@@ -83,6 +84,11 @@ public final class JavaDesignerBindings extends DefaultDesignerBindings {
return new Attribute(node, "SimpleName", node.getSimpleName());
}
+ @Override
+ public Attribute visit(ASTAnnotation node, Void data) {
+ return new Attribute(node, "SimpleName", node.getSimpleName());
+ }
+
@Override
public Attribute visit(ASTMethodDeclaration node, Void data) {
return new Attribute(node, "Name", node.getName());
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java
index 2fda06d48e..ddd4647f35 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java
@@ -17,11 +17,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
-import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral;
import net.sourceforge.pmd.lang.java.ast.Annotatable;
-import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
/**
* Helper methods to suppress violations based on annotations.
@@ -107,9 +106,8 @@ final class AnnotationSuppressionUtil {
// @formatter:on
private static boolean annotationSuppresses(ASTAnnotation annotation, Rule rule) {
- // if (SuppressWarnings.class.equals(getType())) { // typeres is not always on
- if (TypeHelper.isA(annotation, SuppressWarnings.class)) {
- for (ASTLiteral element : annotation.findDescendantsOfType(ASTLiteral.class)) {
+ if (annotation.getSymbol().getBinaryName().equals("java.lang.SuppressWarnings")) {
+ for (ASTStringLiteral element : annotation.findDescendantsOfType(ASTStringLiteral.class)) {
if (element.hasImageEqualTo("\"PMD\"") || element.hasImageEqualTo(
"\"PMD." + rule.getName() + "\"")
// Check for standard annotations values
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java
index 3539ed6f84..155e9acac2 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java
@@ -8,18 +8,20 @@ import static net.sourceforge.pmd.util.CollectionUtil.any;
import java.lang.reflect.Array;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
+import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
-import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.ast.TypeNode;
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration;
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader;
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader.ClassLoaderWrapper;
@@ -91,21 +93,21 @@ public final class TypeHelper {
|| clazzName.equals(n.getName());
}
- private static boolean fallbackIsA(TypeNode n, String clazzName) {
- if (n.getImage() != null && !n.getImage().contains(".") && clazzName.contains(".")) {
- // simple name detected, check the imports to get the full name and use that for fallback
- List imports = n.getRoot().findChildrenOfType(ASTImportDeclaration.class);
- for (ASTImportDeclaration importDecl : imports) {
- if (n.hasImageEqualTo(importDecl.getImportedSimpleName())) {
- // found the import, compare the full names
- return clazzName.equals(importDecl.getImportedName());
- }
- }
- }
+ private static boolean fallbackIsA(final TypeNode n, String clazzName) {
+ // Later we won't need a fallback. Symbols already contain subclass information.
- // fall back on using the simple name of the class only
- if (clazzName.equals(n.getImage()) || clazzName.endsWith("." + n.getImage())) {
+ if (n instanceof ASTAnyTypeDeclaration && ((ASTAnyTypeDeclaration) n).getBinaryName().equals(clazzName)) {
return true;
+ } else if (n instanceof ASTClassOrInterfaceType || n instanceof ASTAnnotation) {
+ ASTClassOrInterfaceType classType;
+ if (n instanceof ASTAnnotation) {
+ classType = ((ASTAnnotation) n).getTypeNode();
+ } else {
+ classType = (ASTClassOrInterfaceType) n;
+ }
+
+ JTypeDeclSymbol sym = classType.getReferencedSym();
+ return sym instanceof JClassSymbol && ((JClassSymbol) sym).getBinaryName().equals(clazzName);
}
if (n instanceof ASTClassOrInterfaceDeclaration) {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelperTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelperTest.java
index 5142b05b96..fae0d56ac5 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelperTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelperTest.java
@@ -93,8 +93,7 @@ public class TypeHelperTest extends BaseNonParserTest {
Assert.assertNull(annotation.getType());
Assert.assertTrue(TypeHelper.isA(annotation, "foo.Stuff"));
Assert.assertFalse(TypeHelper.isA(annotation, "other.Stuff"));
- // if the searched class name is not fully qualified, then the search should still be successfull
- Assert.assertTrue(TypeHelper.isA(annotation, "Stuff"));
+ Assert.assertFalse(TypeHelper.isA(annotation, "Stuff"));
}
private void assertIsA(TypeNode node, Class> type) {
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt
index 1a9685cd76..0d13e0943c 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt
@@ -31,18 +31,20 @@ class ASTAnnotationTest : ParserTestSpec({
"@F" should parseAs {
child {
- it::getAnnotationName shouldBe "F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe classType("F")
+
it::getMemberList shouldBe null
}
}
"@java.lang.Override" should parseAs {
child {
- it::getAnnotationName shouldBe "java.lang.Override"
it::getSimpleName shouldBe "Override"
+ it::getTypeNode shouldBe qualClassType("java.lang.Override", disambiguated = false)
+
it::getMemberList shouldBe null
}
}
@@ -56,9 +58,10 @@ class ASTAnnotationTest : ParserTestSpec({
"@F(\"ohio\")" should parseAs {
child {
- it::getAnnotationName shouldBe "F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe classType("F")
+
it::getMemberList shouldBe child {
shorthandMemberValue {
stringLit("\"ohio\"")
@@ -69,9 +72,10 @@ class ASTAnnotationTest : ParserTestSpec({
"@org.F({java.lang.Math.PI})" should parseAs {
child {
- it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe qualClassType("org.F", disambiguated = false)
+
it::getMemberList shouldBe child {
shorthandMemberValue {
child {
@@ -87,9 +91,9 @@ class ASTAnnotationTest : ParserTestSpec({
"@org.F({@Aha, @Oh})" should parseAs {
child {
- it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe qualClassType("org.F", disambiguated = false)
it::getMemberList shouldBe child {
shorthandMemberValue {
@@ -103,9 +107,9 @@ class ASTAnnotationTest : ParserTestSpec({
}
"@org.F(@Oh)" should parseAs {
child {
- it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe qualClassType("org.F", disambiguated = false)
it::getMemberList shouldBe child {
shorthandMemberValue {
@@ -124,9 +128,9 @@ class ASTAnnotationTest : ParserTestSpec({
"@F(a=\"ohio\")" should parseAs {
child {
- it::getAnnotationName shouldBe "F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe classType("F")
it::getMemberList shouldBe child {
memberValuePair("a") {
@@ -138,9 +142,9 @@ class ASTAnnotationTest : ParserTestSpec({
"@org.F(a={java.lang.Math.PI}, b=2)" should parseAs {
child {
- it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
+ it::getTypeNode shouldBe qualClassType("org.F", disambiguated = false)
it::getMemberList shouldBe child {
memberValuePair("a") {
@@ -167,6 +171,8 @@ class ASTAnnotationTest : ParserTestSpec({
child {
+ it::getTypeNode shouldBe classType("TestAnnotation")
+
it::getMemberList shouldBe child {
shorthandMemberValue {
@@ -174,6 +180,8 @@ class ASTAnnotationTest : ParserTestSpec({
child {
annotation {
+ it::getTypeNode shouldBe classType("SuppressWarnings")
+
it::getMemberList shouldBe child {
shorthandMemberValue {
child {}
@@ -181,6 +189,9 @@ class ASTAnnotationTest : ParserTestSpec({
}
}
annotation {
+
+ it::getTypeNode shouldBe classType("SuppressWarnings")
+
it::getMemberList shouldBe child {
memberValuePair("value") {
it::isShorthand shouldBe false
@@ -191,6 +202,9 @@ class ASTAnnotationTest : ParserTestSpec({
}
}
annotation {
+
+ it::getTypeNode shouldBe classType("SuppressWarnings")
+
it::getMemberList shouldBe child {
shorthandMemberValue {
child {
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt
index c15d0803bf..c2263b891d 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt
@@ -7,17 +7,13 @@ import io.kotlintest.matchers.collections.shouldBeEmpty
import io.kotlintest.matchers.types.shouldBeInstanceOf
import io.kotlintest.shouldNotBe
import net.sourceforge.pmd.internal.util.IteratorUtil
-import net.sourceforge.pmd.lang.ast.GenericToken
import net.sourceforge.pmd.lang.ast.Node
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken
import net.sourceforge.pmd.lang.ast.test.NodeSpec
import net.sourceforge.pmd.lang.ast.test.ValuedNodeSpec
import net.sourceforge.pmd.lang.ast.test.shouldBe
-import net.sourceforge.pmd.lang.ast.test.shouldMatch
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType.*
-import java.util.*
-import kotlin.reflect.KCallable
fun > C?.shouldContainAtMostOneOf(vararg expected: T) {
this shouldNotBe null
@@ -69,9 +65,9 @@ fun TreeNodeWrapper.annotation(spec: ValuedNodeSpec.annotation(name: String, spec: NodeSpec = EmptyAssertions) =
+fun TreeNodeWrapper.annotation(simpleName: String, spec: NodeSpec = EmptyAssertions) =
child(ignoreChildren = spec == EmptyAssertions) {
- it::getAnnotationName shouldBe name
+ it::getSimpleName shouldBe simpleName
spec()
}
@@ -375,10 +371,16 @@ fun TreeNodeWrapper.classType(simpleName: String, contents: NodeSpec.qualClassType(canoName: String, contents: NodeSpec = EmptyAssertions) =
+fun TreeNodeWrapper.qualClassType(canoName: String, disambiguated: Boolean = true, contents: NodeSpec = EmptyAssertions) =
child(ignoreChildren = contents == EmptyAssertions) {
- it::getImage shouldBe canoName
- it::getSimpleName shouldBe canoName.substringAfterLast('.')
+ val simpleName = canoName.substringAfterLast('.')
+ if (disambiguated) {
+ it::getImage shouldBe canoName
+ it::getSimpleName shouldBe simpleName
+ } else {
+ it::getImage shouldBe simpleName
+ it::getTypeImage shouldBe canoName
+ }
contents()
}
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt
index d0df3f1954..a3cda4a767 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.symbols.table.internal
+import io.kotlintest.matchers.types.shouldBeSameInstanceAs
import io.kotlintest.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBeA
@@ -190,6 +191,7 @@ class TypeParamScopingTest : ParserTestSpec({
val annot = acu.descendants(ASTAnnotation::class.java).first()!!
annot.symbolTable.shouldResolveTypeTo("T", t.symbol) //not t2
+ annot.symbol.shouldBeSameInstanceAs(t.symbol)
}
doTest("Local class shadows type param") {