Java: Mark service implementations as used if the service declaration is used - added test cases, updated test data (IDEA-169204)

This commit is contained in:
Pavel Dolgov
2017-04-13 17:19:29 +03:00
parent b33785c618
commit 5906bcbd32
16 changed files with 259 additions and 24 deletions
@@ -0,0 +1,8 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt implements MyService {
public MyServiceExt() {}
@Override
public void foo() {}
}
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
</problems>
@@ -0,0 +1,8 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt implements MyService {
public MyServiceExt() {}
@Override
public void foo() {}
}
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
</problems>
@@ -0,0 +1,12 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt {
public static MyService provider() {
return new MyService() {
@Override
public void foo() {
}
};
}
}
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
</problems>
@@ -3,8 +3,36 @@
<problem>
<file>MyServiceImpl.java</file>
<line>5</line>
<entry_point TYPE="java.module" FQNAME="M2" />
<problem_class severity="WARNING">unused</problem_class>
<description>Unused declaration</description>
<package>my.impl</package>
<entry_point TYPE="method" FQNAME="my.impl.MyServiceImpl MyServiceImpl()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Constructor has usage(s) but they all belong to calls chain that has no members reachable from entry points.</description>
</problem>
<problem>
<file>MyServiceImpl.java</file>
<line>4</line>
<package>my.impl</package>
<entry_point TYPE="class" FQNAME="my.impl.MyServiceImpl" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Class has one instantiation but it is not reachable from entry points.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="class" FQNAME="my.api.MyService" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&amp;nbsp;Interface has an implementation but &lt;ul&gt;&lt;li&gt;it is never instantiated OR&lt;/li&gt;&lt;li&gt;no instantiations are reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -0,0 +1,8 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt implements MyService {
public MyServiceExt() {}
@Override
public void foo() {}
}
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>MyServiceExt.java</file>
<line>5</line>
<package>my.ext</package>
<entry_point TYPE="method" FQNAME="my.ext.MyServiceExt MyServiceExt()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Constructor has usage(s) but they all belong to calls chain that has no members reachable from entry points.</description>
</problem>
<problem>
<file>MyServiceExt.java</file>
<line>4</line>
<package>my.ext</package>
<entry_point TYPE="class" FQNAME="my.ext.MyServiceExt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Class has one instantiation but it is not reachable from entry points.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="class" FQNAME="my.api.MyService" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&amp;nbsp;Interface has an implementation but &lt;ul&gt;&lt;li&gt;it is never instantiated OR&lt;/li&gt;&lt;li&gt;no instantiations are reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -0,0 +1,7 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt implements MyService {
@Override
public void foo() {}
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>MyServiceExt.java</file>
<line>4</line>
<package>my.ext</package>
<entry_point TYPE="class" FQNAME="my.ext.MyServiceExt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Class is not instantiated.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -0,0 +1,12 @@
package my.ext;
import my.api.MyService;
public class MyServiceExt {
public static MyService provider() {
return new MyService() {
@Override
public void foo() {
}
};
}
}
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>MyServiceExt.java</file>
<line>5</line>
<package>my.ext</package>
<entry_point TYPE="method" FQNAME="my.ext.MyServiceExt my.api.MyService provider()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Method has one usage but it is not reachable from entry points.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="class" FQNAME="my.api.MyService" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&amp;nbsp;Interface has an implementation but &lt;ul&gt;&lt;li&gt;it is never instantiated OR&lt;/li&gt;&lt;li&gt;no instantiations are reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -3,8 +3,18 @@
<problem>
<file>MyServiceImpl.java</file>
<line>4</line>
<entry_point TYPE="java.module" FQNAME="M2" />
<problem_class severity="WARNING">unused</problem_class>
<description>Unused declaration</description>
<package>my.impl</package>
<entry_point TYPE="class" FQNAME="my.impl.MyServiceImpl" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Class is not instantiated.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -3,8 +3,27 @@
<problem>
<file>MyServiceImpl.java</file>
<line>5</line>
<entry_point TYPE="java.module" FQNAME="M2" />
<problem_class severity="WARNING">unused</problem_class>
<description>Unused declaration</description>
<package>my.impl</package>
<entry_point TYPE="method" FQNAME="my.impl.MyServiceImpl my.api.MyService provider()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>Method has one usage but it is not reachable from entry points.</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="method" FQNAME="my.api.MyService void foo()" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&lt;ul&gt;&lt;li&gt;Method owner class is never instantiated OR&lt;/li&gt;&lt;li&gt;An instantiation is not reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
<problem>
<file>MyService.java</file>
<line>1</line>
<package>my.api</package>
<entry_point TYPE="class" FQNAME="my.api.MyService" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">unused declaration</problem_class>
<description>&amp;nbsp;Interface has an implementation but &lt;ul&gt;&lt;li&gt;it is never instantiated OR&lt;/li&gt;&lt;li&gt;no instantiations are reachable from entry points.&lt;/li&gt;&lt;/ul&gt;</description>
</problem>
</problems>
@@ -16,18 +16,17 @@
package com.intellij.codeInspection
import com.intellij.analysis.AnalysisScope
import com.intellij.idea.Bombed
import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtil.loadFileText
import com.intellij.testFramework.InspectionTestCase
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.createGlobalContextForTool
import com.intellij.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.M2
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.MAIN
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
import org.intellij.lang.annotations.Language
import org.jetbrains.annotations.NonNls
/**
* @author Pavel.Dolgov
@@ -38,8 +37,7 @@ class Java9UnusedServiceImplementationsTest : LightJava9ModulesCodeInsightFixtur
override fun setUp() {
super.setUp()
addFile("module-info.java", "module MAIN { requires M2; }", MAIN)
addFile("module-info.java", "module M2 { exports my.api; provides my.api.MyService with my.impl.MyServiceImpl; }", M2)
moduleInfo("module MAIN { requires API; }", MAIN)
addFile("my/api/MyService.java", "package my.api; public interface MyService { void foo(); }", M2)
}
@@ -50,17 +48,27 @@ class Java9UnusedServiceImplementationsTest : LightJava9ModulesCodeInsightFixtur
fun testProvider() = doTest()
@Bombed(user ="Pavel Dolgov", year = 2017, month = 8, day = 1, description = "Disabled, until the test infrastructure supports it")
fun testUnusedImplementation() = doTest(false)
@Bombed(user ="Pavel Dolgov", year = 2017, month = 8, day = 1, description = "Disabled, until the test infrastructure supports it")
fun testUnusedConstructor() = doTest(false)
@Bombed(user ="Pavel Dolgov", year = 2017, month = 8, day = 1, description = "Disabled, until the test infrastructure supports it")
fun testUnusedProvider() = doTest(false)
private fun doTest(withUsage: Boolean = true) {
@Language("JAVA") @NonNls
fun testExternalImplementation() = doTest(sameModule = false)
fun testExternalConstructor() = doTest(sameModule = false)
fun testExternalProvider() = doTest(sameModule = false)
fun testUnusedExternalImplementation() = doTest(false, sameModule = false)
fun testUnusedExternalConstructor() = doTest(false, sameModule = false)
fun testUnusedExternalProvider() = doTest(false, sameModule = false)
private fun doTest(withUsage: Boolean = true, sameModule: Boolean = true) {
@Language("JAVA")
val usageText = """
import my.api.MyService;
public class MyApp {
@@ -72,11 +80,26 @@ class Java9UnusedServiceImplementationsTest : LightJava9ModulesCodeInsightFixtur
}"""
if (withUsage) addFile("my/app/MyApp.java", usageText, MAIN)
if (sameModule) {
moduleInfo("module API { exports my.api; provides my.api.MyService with my.impl.MyServiceImpl; }", M2)
}
else {
val moduleManager = ModuleManager.getInstance(project)
val m2 = moduleManager.findModuleByName(M2.moduleName)!!
val m4 = moduleManager.findModuleByName(M4.moduleName)!!
ModuleRootModificationUtil.addDependency(m4, m2)
moduleInfo("module API { exports my.api; }", M2)
moduleInfo("module EXT { requires API; provides my.api.MyService with my.ext.MyServiceExt; }", M4)
}
val testPath = testDataPath + "/" + getTestName(true)
val sourceFile = FileUtil.findFirstThatExist(testPath + "/MyServiceImpl.java")
val sourceFile = FileUtil.findFirstThatExist("$testPath/MyService${if (sameModule) "Impl" else "Ext"}.java")
assertNotNull("Test data: $testPath", sourceFile)
val implText = String(loadFileText(sourceFile!!))
addFile("my/impl/MyServiceImpl.java", implText, M2)
val implText = String(FileUtil.loadFileText(sourceFile!!))
if (sameModule)
addFile("my/impl/MyServiceImpl.java", implText, M2)
else
addFile("my/ext/MyServiceExt.java", implText, M4)
val toolWrapper = InspectionTestCase.getUnusedDeclarationWrapper()
val scope = AnalysisScope(project)
@@ -84,4 +107,8 @@ class Java9UnusedServiceImplementationsTest : LightJava9ModulesCodeInsightFixtur
InspectionTestUtil.runTool(toolWrapper, scope, globalContext)
InspectionTestUtil.compareToolResults(globalContext, toolWrapper, true, testPath)
}
private fun moduleInfo(@Language("JAVA") moduleInfoText: String, descriptor: ModuleDescriptor) {
addFile("module-info.java", moduleInfoText, descriptor)
}
}