Implement inspection for declarations with implicit platform types #KT-12310 Fixed

(cherry picked from commit 00d8c26)
This commit is contained in:
Kirill Rakhman
2016-06-07 00:35:31 +03:00
committed by Mikhail Glukhikh
parent 1cb72169f9
commit d7bf03be46
11 changed files with 164 additions and 0 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports functions and properties that have platform type. In order to prevent unexpected errors, the type should be declared explicitly.
</body>
</html>
+8
View File
@@ -1569,6 +1569,14 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.HasPlatformTypeInspection"
displayName="Function or property has platform type"
groupName="Kotlin"
enabledByDefault="true"
level="WEAK WARNING"
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.CanBePrimaryConstructorPropertyInspection"
displayName="Property is explicitly assigned to constructor parameter"
groupName="Kotlin"
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2016 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.IntentionWrapper
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.isNullabilityFlexible
class HasPlatformTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
super.visitDeclaration(declaration)
val declType = when (declaration) {
is KtFunction -> 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() }
}
}
}
}
+3
View File
@@ -0,0 +1,3 @@
// WITH_RUNTIME
fun foo() = java.lang.String.valueOf(1)
@@ -0,0 +1,26 @@
<problems>
<problem>
<file>function.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/function.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Function, property or variable has platform type.</problem_class>
<description>Function has platform type. Make the type explicit to prevent subtle bugs.</description>
</problem>
<problem>
<file>property1.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/property1.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Function, property or variable has platform type.</problem_class>
<description>Property has platform type. Make the type explicit to prevent subtle bugs.</description>
</problem>
<problem>
<file>property2.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/property2.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Function, property or variable has platform type.</problem_class>
<description>Property has platform type. Make the type explicit to prevent subtle bugs.</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.HasPlatformTypeInspection
+13
View File
@@ -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)
}
}
@@ -0,0 +1,3 @@
// WITH_RUNTIME
val bar = java.lang.String.valueOf(1)
@@ -0,0 +1,3 @@
// WITH_RUNTIME
val list = arrayOf(java.lang.String.valueOf(1))
+7
View File
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Wrapper<T>(val value: T)
fun star(): Wrapper<Wrapper<*>> = Wrapper(Wrapper(Any()))
fun star2(): List<*> = listOf(Any())
@@ -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");