From 838caff522c9c47b3147d6d0d125c38e3d5561e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 3 May 2023 21:50:18 +0200 Subject: [PATCH] Fix scope of static interface methods --- .../symbols/table/internal/JavaResolvers.java | 8 +++ .../table/internal/MemberInheritanceTest.kt | 53 +++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaResolvers.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaResolvers.java index ee8fb653b7..58ca8ae7d1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaResolvers.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaResolvers.java @@ -128,9 +128,17 @@ public final class JavaResolvers { return t.streamMethods( it -> it.nameEquals(simpleName) && isAccessibleIn(nestRoot, it, true) // fetch protected methods + && isNotStaticInterfaceMethod(it) ).collect(OverloadSet.collectMostSpecific(t)); // remove overridden, hidden methods } + // Static interface methods are not inherited and are in fact not in scope in the subtypes. + // They must be explicitly qualified or imported. + private boolean isNotStaticInterfaceMethod(JMethodSymbol it) { + return !it.isStatic() || it.getEnclosingClass() == t.getSymbol() + || !it.getEnclosingClass().isInterface(); + } + @Override public String toString() { return "methods of " + t; diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt index 090ff282b0..233f4cc299 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt @@ -15,10 +15,7 @@ import net.sourceforge.pmd.lang.ast.test.component8 import net.sourceforge.pmd.lang.ast.test.shouldMatchN import net.sourceforge.pmd.lang.java.ast.* import net.sourceforge.pmd.lang.java.symbols.table.coreimpl.ShadowChain -import net.sourceforge.pmd.lang.java.types.JClassType -import net.sourceforge.pmd.lang.java.types.JVariableSig -import net.sourceforge.pmd.lang.java.types.shouldHaveType -import net.sourceforge.pmd.lang.java.types.typeDsl +import net.sourceforge.pmd.lang.java.types.* @Suppress("UNUSED_VARIABLE") class MemberInheritanceTest : ParserTestSpec({ @@ -765,4 +762,52 @@ class Top { importedMethodCall.methodType.symbol shouldBe importedMethodSym } + + parserTest("Static methods of interfaces are not in scope in subclasses") { + // This is what allows the import below to not be shadowed by the inherited declaration + // This was tested with javac. The intellij compiler doesn't understand this code. + + val acu = parser.withProcessing().parse(""" +package p; + +import static p.Top2.foo; + +class Klass implements Top { + static { + foo(); // This is Top2.foo + } + + static class Child { + { + foo(); // This is also Top2.foo + } + } +} +interface Top { + static void foo() {} + + static void bar() { + foo(); // just test that this is not the import + } +} +interface Top2 { + static void foo() {} +} + """) + + val (fooInTop1, _, fooInTop2) = acu.methodDeclarations().toList() + val (call1, call2, callInBar) = acu.methodCalls().crossFindBoundaries().toList() + + withClue(call1) { + call1.methodType.symbol shouldBe fooInTop2.symbol + } + withClue(call2) { + call2.methodType.symbol shouldBe fooInTop2.symbol + } + withClue(callInBar) { + callInBar.methodType.symbol shouldBe fooInTop1.symbol + } + + } + })