Introduce inspection for nullable extension receiver of inline function

So #KT-22303 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-01-19 11:32:12 +03:00
parent 08d3f20e09
commit c90056f8c4
9 changed files with 174 additions and 1 deletions
+10 -1
View File
@@ -2527,7 +2527,7 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ExplicitThisInspection"
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ExplicitThisInspection"
displayName="Redundant explicit 'this'"
groupPath="Kotlin"
groupName="Redundant constructs"
@@ -2673,6 +2673,15 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PlatformExtensionReceiverOfInlineInspection"
displayName="Unsafe call of inline function with nullable extension receiver"
groupPath="Kotlin"
groupName="Java interop issues"
enabledByDefault="true"
level="WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,100 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.editor.event.DocumentEvent
import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.ui.LabeledComponent
import com.intellij.ui.EditorTextField
import org.intellij.lang.regexp.RegExpFileType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.isNullabilityFlexible
import java.awt.BorderLayout
import java.util.regex.PatternSyntaxException
import javax.swing.JPanel
class PlatformExtensionReceiverOfInlineInspection : AbstractKotlinInspection() {
private var nameRegex: Regex? = defaultNamePattern.toRegex()
var namePattern: String = defaultNamePattern
set(value) {
field = value
nameRegex = try {
value.toRegex()
} catch (e: PatternSyntaxException) {
null
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
if (!expression.languageVersionSettings.supportsFeature(LanguageFeature.NullabilityAssertionOnExtensionReceiver)) {
return
}
val nameRegex = nameRegex
val callExpression = expression.selectorExpression as? KtCallExpression ?: return
val calleeText = callExpression.calleeExpression?.text ?: return
if (nameRegex != null && !nameRegex.matches(calleeText)) {
return
}
val context = expression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = expression.getResolvedCall(context) ?: return
val extensionReceiverType = resolvedCall.extensionReceiver?.type ?: return
if (!extensionReceiverType.isNullabilityFlexible()) return
val descriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!descriptor.isInline) return
val receiverExpression = expression.receiverExpression
holder.registerProblem(
receiverExpression,
"Call of inline function with nullable extension receiver can provoke NPE in Kotlin 1.2+",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(AddExclExclCallFix(receiverExpression), receiverExpression.containingKtFile)
)
}
}
override fun createOptionsPanel() = OptionsPanel(this)
class OptionsPanel internal constructor(owner: PlatformExtensionReceiverOfInlineInspection) : JPanel() {
init {
layout = BorderLayout()
val regexField = EditorTextField(owner.namePattern, null, RegExpFileType.INSTANCE).apply {
setOneLineMode(true)
}
regexField.document.addDocumentListener(object : DocumentListener {
override fun documentChanged(e: DocumentEvent?) {
owner.namePattern = regexField.text
}
})
val labeledComponent = LabeledComponent.create(regexField, "Pattern:", BorderLayout.WEST)
add(labeledComponent, BorderLayout.NORTH)
}
}
companion object {
const val defaultNamePattern = "(toBoolean)|(content.*)"
}
}