Base classes for Kotlin inspections

This commit is contained in:
Alexey Sedunov
2014-03-19 18:40:00 +04:00
parent 48b9dcf5ae
commit a0bc6a7b39
5 changed files with 168 additions and 3 deletions
@@ -101,6 +101,7 @@ import org.jetbrains.jet.plugin.libraries.AbstractDecompiledTextTest
import org.jetbrains.jet.plugin.imports.AbstractOptimizeImportsTest
import org.jetbrains.jet.plugin.debugger.AbstractSmartStepIntoTest
import org.jetbrains.jet.plugin.stubs.AbstractStubBuilderTest
import org.jetbrains.jet.plugin.codeInsight.AbstractJetInspectionTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
@@ -379,6 +380,10 @@ fun main(args: Array<String>) {
model("intentions/attributeCallReplacements/replaceUnaryPrefixIntention", testMethod = "doTestReplaceUnaryPrefixIntention")
model("intentions/attributeCallReplacements/replaceInvokeIntention", testMethod = "doTestReplaceInvokeIntention")
model("intentions/simplifyNegatedBinaryExpressionIntention", testMethod = "doTestSimplifyNegatedBinaryExpressionIntention")
}
testClass(javaClass<AbstractJetInspectionTest>()) {
model("codeInsight/inspections", extension = null, recursive = false)
model("intentions/convertNegatedBooleanSequence", testMethod="doTestConvertNegatedBooleanSequence")
model("intentions/convertNegatedExpressionWithDemorgansLaw", testMethod = "doTestConvertNegatedExpressionWithDemorgansLaw")
model("intentions/swapBinaryExpression", testMethod = "doTestSwapBinaryExpression")
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2014 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.jet.plugin.inspections
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.CustomSuppressableInspectionTool
import com.intellij.psi.PsiElement
import com.intellij.codeInspection.SuppressIntentionAction
import com.intellij.codeInspection.SuppressManager
import com.intellij.codeInsight.daemon.HighlightDisplayKey
public abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSuppressableInspectionTool {
public override fun getSuppressActions(element: PsiElement?): Array<SuppressIntentionAction>? {
return SuppressManager.getInstance()!!.createSuppressActions(HighlightDisplayKey.find(getShortName())!!)
}
public override fun isSuppressedFor(element: PsiElement): Boolean {
return SuppressManager.getInstance()!!.isSuppressedFor(element, getID())
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2014 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.jet.plugin.inspections
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.openapi.project.Project
import com.intellij.codeInspection.ProblemDescriptor
import org.jetbrains.jet.plugin.intentions.JetSelfTargetingIntention
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.Editor
public abstract class IntentionBasedInspection<T: JetElement>(
protected val intention: JetSelfTargetingIntention<T>
) : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object: PsiElementVisitor() {
override fun visitElement(element: PsiElement?) {
if (!intention.elementType.isInstance(element)) return
[suppress("UNCHECKED_CAST")]
val targetElement = element as T
if (!intention.isApplicableTo(targetElement)) return
val fix = object: LocalQuickFix {
override fun getFamilyName(): String {
return getName()
}
override fun getName(): String {
return intention.getText()
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
targetElement.getOrCreateEditor()?.let { editor ->
editor.getCaretModel().moveToOffset(targetElement.getTextOffset())
intention.applyTo(targetElement, editor)
}
}
}
holder.registerProblem(targetElement, intention.getText(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fix)
}
}
}
}
private fun PsiElement.getOrCreateEditor(): Editor? {
val file = getContainingFile()?.getVirtualFile()
if (file == null) return null
val document = FileDocumentManager.getInstance()!!.getDocument(file)
if (document == null) return null
val editorFactory = EditorFactory.getInstance()!!
val editors = editorFactory.getEditors(document)
return if (editors.isEmpty()) editorFactory.createEditor(document) else editors[0]
}
@@ -29,9 +29,9 @@ public abstract class JetSelfTargetingIntention<T: JetElement>(val key: String,
setText(JetBundle.message(key))
}
protected abstract fun isApplicableTo(element: T): Boolean
protected open fun isApplicableTo(element: T, editor: Editor): Boolean = isApplicableTo(element)
protected abstract fun applyTo(element: T, editor: Editor)
abstract fun isApplicableTo(element: T): Boolean
open fun isApplicableTo(element: T, editor: Editor): Boolean = isApplicableTo(element)
abstract fun applyTo(element: T, editor: Editor)
protected fun getTarget(editor: Editor, file: PsiFile): T? {
val offset = editor.getCaretModel().getOffset()
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2014 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.jet.plugin.codeInsight
import java.io.File
import org.jetbrains.jet.InTextDirectivesUtils
import com.intellij.openapi.util.io.FileUtil
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.jet.plugin.JetLightProjectDescriptor
import org.jetbrains.jet.plugin.PluginTestCaseBase
import com.intellij.codeInspection.InspectionEP
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
public abstract class AbstractJetInspectionTest: LightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = JetLightProjectDescriptor.INSTANCE
override fun setUp() {
super.setUp()
myFixture!!.setTestDataPath("${PluginTestCaseBase.getTestDataPathBase()}/codeInsight/inspections")
}
protected fun doTest(path: String) {
val testDir = File(path).getName()
val options = FileUtil.loadFile(File(path, "options.test"), true)
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!)
myFixture!!.testInspection(testDir, LocalInspectionToolWrapper(inspectionClass.newInstance() as LocalInspectionTool))
}
}