a couple of inspections moved to idea-analysis module

This commit is contained in:
Alexey Kudravtsev
2014-08-15 12:33:57 +04:00
parent 6913869b9c
commit 1f48bbb7d3
5 changed files with 8 additions and 9 deletions
@@ -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]
}
@@ -0,0 +1,22 @@
/*
* 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 org.jetbrains.jet.plugin.intentions.RemoveExplicitTypeArguments
import org.jetbrains.jet.lang.psi.JetTypeArgumentList
public class RemoveExplicitTypeArgsInspection : IntentionBasedInspection<JetTypeArgumentList>(RemoveExplicitTypeArguments())
@@ -0,0 +1,22 @@
/*
* 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 org.jetbrains.jet.plugin.intentions.SimplifyNegatedBinaryExpressionIntention
import org.jetbrains.jet.lang.psi.JetPrefixExpression
public class SimplifyBinaryNegationInspection : IntentionBasedInspection<JetPrefixExpression>(SimplifyNegatedBinaryExpressionIntention())
@@ -0,0 +1,95 @@
/*
* 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.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.psi.JetTypeProjection
import org.jetbrains.jet.lang.psi.Call
import java.util.ArrayList
import org.jetbrains.jet.di.InjectorForMacros
import org.jetbrains.jet.lang.resolve.BindingTraceContext
import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession
import org.jetbrains.jet.lang.psi.JetProperty
import org.jetbrains.jet.lang.psi.JetTypeArgumentList
import org.jetbrains.jet.lang.psi.JetReturnExpression
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.jet.lang.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.jet.lang.psi.psiUtil.getTextWithLocation
public class RemoveExplicitTypeArguments : JetSelfTargetingIntention<JetTypeArgumentList>(
"remove.explicit.type.arguments", javaClass()) {
override fun isApplicableTo(element: JetTypeArgumentList): Boolean {
val callExpression = element.getParent()
if (callExpression !is JetCallExpression) return false
val context = AnalyzerFacadeWithCache.getContextForElement(callExpression)
if (callExpression.getTypeArguments().isEmpty()) return false
val resolveSession = callExpression.getLazyResolveSession()
val injector = InjectorForMacros(callExpression.getProject(), resolveSession.getModuleDescriptor())
val scope = context[BindingContext.RESOLUTION_SCOPE, callExpression]
val originalCall = callExpression.getResolvedCall(context)
if (originalCall == null || scope !is JetScope) return false
val untypedCall = CallWithoutTypeArgs(originalCall.getCall())
// todo Check with expected type for other expressions
// If always use expected type from trace there is a problem with nested calls:
// the expression type for them can depend on their explicit type arguments (via outer call),
// therefore we should resolve outer call with erased type arguments for inner call
val parent = callExpression.getParent()
val expectedTypeIsExplicitInCode = when (parent) {
is JetProperty -> parent.getInitializer() == callExpression && parent.getTypeRef() != null
is JetDeclarationWithBody -> parent.getBodyExpression() == callExpression
is JetReturnExpression -> true
else -> false
}
val jType = if (expectedTypeIsExplicitInCode) {
context[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE
}
else {
TypeUtils.NO_EXPECTED_TYPE
}
val dataFlow = context[BindingContext.EXPRESSION_DATA_FLOW_INFO, callExpression] ?: DataFlowInfo.EMPTY
val resolutionResults = injector.getExpressionTypingServices()?.getCallResolver()?.resolveFunctionCall(
BindingTraceContext(), scope, untypedCall, jType, dataFlow, false)
assert (resolutionResults?.isSingleResult() ?: true) { "Removing type arguments changed resolve for: " +
"${callExpression.getTextWithLocation()} to ${resolutionResults?.getResultCode()}" }
val args = originalCall.getTypeArguments()
val newArgs = resolutionResults?.getResultingCall()?.getTypeArguments()
return args == newArgs
}
private class CallWithoutTypeArgs(call: Call) : DelegatingCall(call) {
override fun getTypeArguments(): List<JetTypeProjection> = listOf()
override fun getTypeArgumentList() = null
}
override fun applyTo(element: JetTypeArgumentList, editor: Editor) {
element.delete()
}
}
@@ -0,0 +1,95 @@
/*
* 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.intentions
import org.jetbrains.jet.lang.psi.JetPrefixExpression
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetParenthesizedExpression
import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.jet.lexer.JetSingleValueToken
import org.jetbrains.jet.lang.psi.JetOperationExpression
import org.jetbrains.jet.lang.psi.JetIsExpression
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.plugin.JetBundle
import org.jetbrains.jet.lang.psi.JetPsiFactory
public class SimplifyNegatedBinaryExpressionIntention : JetSelfTargetingIntention<JetPrefixExpression>("simplify.negated.binary.expression", javaClass()) {
private fun JetPrefixExpression.unparenthesize(): JetExpression? {
return (this.getBaseExpression() as? JetParenthesizedExpression)?.getExpression()
}
public fun JetToken.negate(): JetSingleValueToken? = when (this) {
JetTokens.IN_KEYWORD -> JetTokens.NOT_IN
JetTokens.NOT_IN -> JetTokens.IN_KEYWORD
JetTokens.IS_KEYWORD -> JetTokens.NOT_IS
JetTokens.NOT_IS -> JetTokens.IS_KEYWORD
JetTokens.EQEQ -> JetTokens.EXCLEQ
JetTokens.EXCLEQ -> JetTokens.EQEQ
JetTokens.LT -> JetTokens.GTEQ
JetTokens.GTEQ -> JetTokens.LT
JetTokens.GT -> JetTokens.LTEQ
JetTokens.LTEQ -> JetTokens.GT
else -> null
}
override fun isApplicableTo(element: JetPrefixExpression): Boolean {
if (element.getOperationReference().getReferencedNameElementType() != JetTokens.EXCL) return false
val expression = element.unparenthesize() as? JetOperationExpression
if (!(expression is JetIsExpression || expression is JetBinaryExpression)) return false
val operation = (JetPsiUtil.getOperationToken(expression) as? JetSingleValueToken) ?: return false
val negOperation = operation.negate() ?: return false
setText(JetBundle.message(key, operation.getValue(), negOperation.getValue()))
return true
}
override fun applyTo(element: JetPrefixExpression, editor: Editor) {
// Guaranteed to succeed (by isApplicableTo)
val expression = element.unparenthesize()!!
val invertedOperation = JetPsiUtil.getOperationToken(expression as JetOperationExpression)!!.negate()!!
val psiFactory = JetPsiFactory(expression)
element.replace(
when (expression) {
is JetIsExpression -> {
psiFactory.createExpression(
"${expression.getLeftHandSide().getText() ?: ""} ${invertedOperation.getValue()} ${expression.getTypeRef()?.getText() ?: ""}"
)
}
is JetBinaryExpression -> psiFactory.createBinaryExpression(
expression.getLeft(),
invertedOperation.getValue(),
expression.getRight()
)
else -> throw IllegalStateException(
"Expression is neither a JetIsExpression or JetBinaryExpression (checked by isApplicableTo): ${expression.getText()}"
)
}
)
}
}