Add inspection for usages of Kotlin internal declarations in Java
#KT-11393 Fixed
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports usages of Kotlin internal declarations from Java code in different module.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>
|
||||
+1
@@ -0,0 +1 @@
|
||||
internal class SomeInternalClass
|
||||
+17
@@ -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>
|
||||
+1
@@ -0,0 +1 @@
|
||||
internal class SomeInternalClassInOtherModule
|
||||
@@ -0,0 +1,72 @@
|
||||
<problems>
|
||||
<problem>
|
||||
<file>test.java</file>
|
||||
<line>3</line>
|
||||
<module>A</module>
|
||||
<package><default></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><default></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><default></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><default></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><default></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><default></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><default></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>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinInternalInJavaInspection",
|
||||
"withRuntime": "true",
|
||||
"isMultiModule": "true"
|
||||
}
|
||||
+14
-10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user