Add inspection for usages of Kotlin internal declarations in Java

#KT-11393 Fixed
This commit is contained in:
Vyacheslav Gerasimov
2017-03-16 13:33:07 +03:00
parent bd53922c64
commit a795313c7d
12 changed files with 206 additions and 10 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports usages of Kotlin internal declarations from Java code in different module.
</body>
</html>
+8
View File
@@ -2074,6 +2074,14 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinInternalInJavaInspection"
displayName="Usage of Kotlin internal declarations from Java"
groupName="Kotlin"
enabledByDefault="true"
level="ERROR"
language="JAVA"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo
import org.jetbrains.kotlin.lexer.KtTokens.INTERNAL_KEYWORD
import org.jetbrains.kotlin.psi.KtModifierListOwner
class KotlinInternalInJavaInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitReferenceExpression(expression: PsiReferenceExpression?) {
expression?.checkAndReport(holder)
}
override fun visitReferenceElement(reference: PsiJavaCodeReferenceElement?) {
reference?.checkAndReport(holder)
}
}
}
private fun PsiElement.checkAndReport(holder: ProblemsHolder) {
val lightElement = (this as? PsiReference)?.resolve() as? KtLightElement<*, *> ?: return
val modifierListOwner = lightElement.kotlinOrigin as? KtModifierListOwner ?: return
if(inSameModule(modifierListOwner)) {
return
}
if(modifierListOwner.hasModifier(INTERNAL_KEYWORD)) {
holder.registerProblem(this, "Usage of Kotlin internal declaration from different module")
}
}
private fun PsiElement.inSameModule(element: PsiElement) = getNullableModuleInfo()?.equals(element.getNullableModuleInfo()) ?: true
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="B" />
</component>
</module>
@@ -0,0 +1 @@
internal class SomeInternalClass
@@ -0,0 +1,17 @@
import kotlin.collections.MapsKt;
import kotlin.internal.PlatformImplementations; // Internal in stdlib, error
import java.util.function.Consumer;
public class SomeClass extends PlatformImplementations { // Internal in stdlib, error
public static void doSomething() {
PlatformImplementations a; // Internal in stdlib, error
Integer c;
MapsKt.mapCapacity(3); // Internal in stdlib, error
Consumer<Integer> fun = MapsKt::mapCapacity; // Internal in stdlib, error
SomeInternalClass b = new SomeInternalClass(); // Internal in same module, OK
SomeInternalClassInOtherModule b = new SomeInternalClassInOtherModule(); // Internal in other module, error
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1 @@
internal class SomeInternalClassInOtherModule
@@ -0,0 +1,72 @@
<problems>
<problem>
<file>test.java</file>
<line>3</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>7</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>9</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>11</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>12</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>15</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
<problem>
<file>test.java</file>
<line>15</line>
<module>A</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="test.java" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Usage of Kotlin internal declarations from Java</problem_class>
<description>Usage of Kotlin internal declaration from different module</description>
</problem>
</problems>
@@ -0,0 +1,5 @@
{
"inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinInternalInJavaInspection",
"withRuntime": "true",
"isMultiModule": "true"
}
@@ -24,13 +24,13 @@ import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import org.jetbrains.kotlin.idea.jsonUtils.getString
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import java.io.File
abstract class AbstractMultiFileInspectionTest : KotlinMultiFileTestCase() {
@@ -44,19 +44,18 @@ abstract class AbstractMultiFileInspectionTest : KotlinMultiFileTestCase() {
val inspection = LocalInspectionToolWrapper(Class.forName(config.getString("inspectionClass")).newInstance() as LocalInspectionTool)
val withRuntime = config["withRuntime"]?.asBoolean ?: false
if (withRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
val withFullJdk = config["withFullJdk"]?.asBoolean ?: false
isMultiModule = config["isMultiModule"]?.asBoolean ?: false
doTest({ _, _ ->
try {
if (withRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(
module,
if (withFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk()
)
project.allModules().forEach { module ->
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(
module,
if (withFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk()
)
}
}
val scope = AnalysisScope(myProject)
@@ -74,7 +73,12 @@ abstract class AbstractMultiFileInspectionTest : KotlinMultiFileTestCase() {
}
finally {
if (withRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(module, IdeaTestUtil.getMockJdk17())
project.allModules().forEach { module ->
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(
module,
if (withFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk()
)
}
}
}
},
@@ -42,6 +42,12 @@ public class MultiFileInspectionTestGenerated extends AbstractMultiFileInspectio
doTest(fileName);
}
@TestMetadata("kotlinInternalInJava/kotlinInternalInJava.test")
public void testKotlinInternalInJava_KotlinInternalInJava() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileInspections/kotlinInternalInJava/kotlinInternalInJava.test");
doTest(fileName);
}
@TestMetadata("mismatchedProjectAndDirectory/mismatchedProjectAndDirectory.test")
public void testMismatchedProjectAndDirectory_MismatchedProjectAndDirectory() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileInspections/mismatchedProjectAndDirectory/mismatchedProjectAndDirectory.test");