diff --git a/idea/resources/inspectionDescriptions/HasPlatformType.html b/idea/resources/inspectionDescriptions/HasPlatformType.html
new file mode 100644
index 00000000000..5fdc2b7ccd5
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/HasPlatformType.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports functions and properties that have platform type. In order to prevent unexpected errors, the type should be declared explicitly.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index eba7789b6d1..edde68e5111 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -1569,6 +1569,14 @@
language="kotlin"
/>
+
+
if (declaration.isLocal || declaration.hasDeclaredReturnType()) return else "Function"
+ is KtProperty -> if (declaration.isLocal || declaration.typeReference != null) return else "Property"
+ else -> return
+ }
+
+ if (declaration.containingClassOrObject?.isLocal() ?: false) return
+
+ val context = declaration.analyze(BodyResolveMode.PARTIAL)
+ val returnType = declaration.getReturnType(context) ?: return
+ if (!returnType.isFlexibleRecursive()) return
+
+ val fixes = mutableListOf(IntentionWrapper(SpecifyTypeExplicitlyIntention(), declaration.containingFile))
+
+ if (returnType.isNullabilityFlexible()) {
+ val expression = declaration.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
+ if (expression != null) {
+ fixes += IntentionWrapper(AddExclExclCallFix(expression), declaration.containingFile)
+ }
+ }
+
+ val nameElement = declaration.nameIdentifier ?: return
+ val problemDescriptor = holder.manager.createProblemDescriptor(
+ nameElement,
+ nameElement,
+ "$declType has platform type. Make the type explicit to prevent subtle bugs.",
+ ProblemHighlightType.WEAK_WARNING,
+ isOnTheFly,
+ *fixes.toTypedArray()
+ )
+ holder.registerProblem(problemDescriptor)
+ }
+
+ private fun KtDeclaration.getReturnType(bindingContext: BindingContext): KotlinType? {
+ val callable = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? CallableDescriptor ?: return null
+ return callable.returnType
+ }
+
+ private fun KotlinType.isFlexibleRecursive(): Boolean {
+ if (isFlexible()) return true
+ return arguments.any { it.type.isFlexibleRecursive() }
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/function.kt b/idea/testData/inspections/hasPlatformType/function.kt
new file mode 100644
index 00000000000..c41deedf2c1
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/function.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+fun foo() = java.lang.String.valueOf(1)
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/inspectionData/expected.xml b/idea/testData/inspections/hasPlatformType/inspectionData/expected.xml
new file mode 100644
index 00000000000..f4f660b9d2e
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/inspectionData/expected.xml
@@ -0,0 +1,26 @@
+
+
+ function.kt
+ 3
+ light_idea_test_case
+
+ Function, property or variable has platform type.
+ Function has platform type. Make the type explicit to prevent subtle bugs.
+
+
+ property1.kt
+ 3
+ light_idea_test_case
+
+ Function, property or variable has platform type.
+ Property has platform type. Make the type explicit to prevent subtle bugs.
+
+
+ property2.kt
+ 3
+ light_idea_test_case
+
+ Function, property or variable has platform type.
+ Property has platform type. Make the type explicit to prevent subtle bugs.
+
+
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/inspectionData/inspections.test b/idea/testData/inspections/hasPlatformType/inspectionData/inspections.test
new file mode 100644
index 00000000000..a40fc8b49dc
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.HasPlatformTypeInspection
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/local.kt b/idea/testData/inspections/hasPlatformType/local.kt
new file mode 100644
index 00000000000..d2cc1014e43
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/local.kt
@@ -0,0 +1,13 @@
+// WITH_RUNTIME
+
+fun foo() {
+ val local = java.lang.String.valueOf(1)
+
+ fun bar() = java.lang.String.valueOf(2)
+
+ class Local {
+ val local = java.lang.String.valueOf(3)
+
+ fun bar() = java.lang.String.valueOf(4)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/property1.kt b/idea/testData/inspections/hasPlatformType/property1.kt
new file mode 100644
index 00000000000..f54eb28efe4
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/property1.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val bar = java.lang.String.valueOf(1)
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/property2.kt b/idea/testData/inspections/hasPlatformType/property2.kt
new file mode 100644
index 00000000000..72602113a6f
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/property2.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val list = arrayOf(java.lang.String.valueOf(1))
\ No newline at end of file
diff --git a/idea/testData/inspections/hasPlatformType/star.kt b/idea/testData/inspections/hasPlatformType/star.kt
new file mode 100644
index 00000000000..c6e71914b5a
--- /dev/null
+++ b/idea/testData/inspections/hasPlatformType/star.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+
+class Wrapper(val value: T)
+
+fun star(): Wrapper> = Wrapper(Wrapper(Any()))
+
+fun star2(): List<*> = listOf(Any())
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index 4c137231dd8..ae1625946b3 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -154,6 +154,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
+ @TestMetadata("hasPlatformType/inspectionData/inspections.test")
+ public void testHasPlatformType_inspectionData_Inspections_test() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/hasPlatformType/inspectionData/inspections.test");
+ doTest(fileName);
+ }
+
@TestMetadata("leakingThis/inspectionData/inspections.test")
public void testLeakingThis_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/leakingThis/inspectionData/inspections.test");