diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index afe0805238..e3c8eed6b0 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -2025,12 +2025,12 @@ void ClassOrInterfaceType() #void: } /* - Now if there are other segments, either the previous type specified formal parameters, + Now if there are other segments, either the previous type specified type arguments, or the next has an annotation. Each of the following segments is its own ClassOrInterfaceType which encloses the previous one. The resulting structure appears left-recursive, but the parser just - executes a loop. That scheme preserves the position of type params and annotations. + executes a loop. That scheme preserves the position of type arguments and annotations. See #1150. FIXME this doesn't account for annotated AND qualified types @@ -2068,6 +2068,10 @@ void TypeArguments(): "<" {checkForBadDiamondUsage();} ">" } +/** +* We could make this #void and instead have a node WildcardType extending ReferenceType. +* This would remove this level of nesting, which is unnecessary. +*/ void TypeArgument(): {} { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceTypeTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceTypeTest.kt index df04bcba86..7b4884744c 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceTypeTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceTypeTest.kt @@ -10,7 +10,7 @@ import io.kotlintest.specs.FunSpec class ASTClassOrInterfaceTypeTest : FunSpec({ - testGroup("Test non-recursive ClassOrInterfaceTypes") { + testGroup("Test non-recursive COITs") { "java.util.List" should matchType { it.typeImage shouldBe "java.util.List" @@ -29,5 +29,76 @@ class ASTClassOrInterfaceTypeTest : FunSpec({ } } + testGroup("Test recursive COITs") { + + "java.util.Map.@Foo Entry" should matchType { + it.typeImage shouldBe "java.util.Map.Entry" + it.image shouldBe "Entry" + + it.leftHandSide shouldBePresent child { + it.typeImage shouldBe "java.util.Map" + } + + child { + it.annotationName shouldBe "Foo" + + child { + child { } + } + } + + child { + child { + child { + it.typeImage shouldBe "K" + } + } + + child { + child { + it.typeImage shouldBe "V" + } + } + } + } + + "Foo.@A Bar.Brew" should matchType { + + it.typeImage shouldBe "Foo.Bar.Brew" + + it.leftHandSide shouldBePresent child { + it.typeImage shouldBe "Foo.Bar" + + it.leftHandSide shouldBePresent child { + it.typeImage shouldBe "Foo" + + child { + child { + child { + it.typeImage shouldBe "K" + } + } + } + } + + child { + it.annotationName shouldBe "A" + + child { + child { } + } + } + } + + child { + child { + child { + it.typeImage shouldBe "V" + } + } + } + } + } + }) \ No newline at end of file