diff --git a/idea/resources/inspectionDescriptions/KotlinInternalInJava.html b/idea/resources/inspectionDescriptions/KotlinInternalInJava.html new file mode 100644 index 00000000000..0644bcf0d22 --- /dev/null +++ b/idea/resources/inspectionDescriptions/KotlinInternalInJava.html @@ -0,0 +1,5 @@ + + +This inspection reports usages of Kotlin internal declarations from Java code in different module. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 83a16bc9a23..f4b715320e7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2074,6 +2074,14 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinInternalInJavaInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinInternalInJavaInspection.kt new file mode 100644 index 00000000000..686fe979fdc --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinInternalInJavaInspection.kt @@ -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 +} diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/A.iml b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/A.iml new file mode 100644 index 00000000000..0f75d4273fc --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/A.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/SomeInternalClass.kt b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/SomeInternalClass.kt new file mode 100644 index 00000000000..2503e5ca423 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/SomeInternalClass.kt @@ -0,0 +1 @@ +internal class SomeInternalClass \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/test.java b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/test.java new file mode 100644 index 00000000000..7901a490090 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/before/A/src/test.java @@ -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 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 + } +} \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/B.iml b/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/B.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/B.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/src/SomeInternalClassInOtherModule.kt b/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/src/SomeInternalClassInOtherModule.kt new file mode 100644 index 00000000000..d5468ede628 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/src/SomeInternalClassInOtherModule.kt @@ -0,0 +1 @@ +internal class SomeInternalClassInOtherModule \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/expected.xml b/idea/testData/multiFileInspections/kotlinInternalInJava/expected.xml new file mode 100644 index 00000000000..7a83f9a08f7 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/expected.xml @@ -0,0 +1,72 @@ + + + test.java + 3 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 7 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 9 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 11 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 12 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 15 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + + test.java + 15 + A + <default> + + Usage of Kotlin internal declarations from Java + Usage of Kotlin internal declaration from different module + + + \ No newline at end of file diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/kotlinInternalInJava.test b/idea/testData/multiFileInspections/kotlinInternalInJava/kotlinInternalInJava.test new file mode 100644 index 00000000000..19c36bbaf48 --- /dev/null +++ b/idea/testData/multiFileInspections/kotlinInternalInJava/kotlinInternalInJava.test @@ -0,0 +1,5 @@ +{ + "inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinInternalInJavaInspection", + "withRuntime": "true", + "isMultiModule": "true" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMultiFileInspectionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMultiFileInspectionTest.kt index 5c91d5c2a15..351cd852c01 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMultiFileInspectionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMultiFileInspectionTest.kt @@ -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() + ) + } } } }, diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java index fd0c7484873..7782a0886ae 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java @@ -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");