a couple of inspections moved to idea-analysis module
This commit is contained in:
+80
@@ -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]
|
||||
}
|
||||
+22
@@ -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())
|
||||
+22
@@ -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())
|
||||
+95
@@ -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()
|
||||
}
|
||||
}
|
||||
+95
@@ -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()}"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user