API for building visitors from lambdas

This commit is contained in:
Dmitry Jemerov
2018-01-05 15:06:03 +01:00
parent 46ac14198c
commit d15fa83749
52 changed files with 1532 additions and 1865 deletions
@@ -0,0 +1,396 @@
/*
* 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.psi
fun classOrObjectVisitor(block: (KtClassOrObject) -> Unit) =
object : KtVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
block(classOrObject)
}
}
fun classOrObjectRecursiveVisitor(block: (KtClassOrObject) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
super.visitClassOrObject(classOrObject)
block(classOrObject)
}
}
fun classVisitor(block: (KtClass) -> Unit) =
object : KtVisitorVoid() {
override fun visitClass(klass: KtClass) {
block(klass)
}
}
fun classRecursiveVisitor(block: (KtClass) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitClass(klass: KtClass) {
super.visitClass(klass)
block(klass)
}
}
fun expressionVisitor(block: (KtExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitExpression(expression: KtExpression) {
block(expression)
}
}
fun expressionRecursiveVisitor(block: (KtExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitExpression(expression: KtExpression) {
super.visitExpression(expression)
block(expression)
}
}
fun parameterVisitor(block: (KtParameter) -> Unit) =
object : KtVisitorVoid() {
override fun visitParameter(parameter: KtParameter) {
block(parameter)
}
}
fun parameterRecursiveVisitor(block: (KtParameter) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitParameter(parameter: KtParameter) {
super.visitParameter(parameter)
block(parameter)
}
}
fun propertyVisitor(block: (KtProperty) -> Unit) =
object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
block(property)
}
}
fun propertyRecursiveVisitor(block: (KtProperty) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitProperty(property: KtProperty) {
super.visitProperty(property)
block(property)
}
}
fun ifExpressionVisitor(block: (KtIfExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitIfExpression(ifExpression: KtIfExpression) {
block(ifExpression)
}
}
fun ifExpressionRecursiveVisitor(block: (KtIfExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitIfExpression(ifExpression: KtIfExpression) {
super.visitIfExpression(ifExpression)
block(ifExpression)
}
}
fun callExpressionVisitor(block: (KtCallExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitCallExpression(callExpression: KtCallExpression) {
block(callExpression)
}
}
fun callExpressionRecursiveVisitor(block: (KtCallExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitCallExpression(callExpression: KtCallExpression) {
super.visitCallExpression(callExpression)
block(callExpression)
}
}
fun primaryConstructorVisitor(block: (KtPrimaryConstructor) -> Unit) =
object : KtVisitorVoid() {
override fun visitPrimaryConstructor(primaryConstructor: KtPrimaryConstructor) {
block(primaryConstructor)
}
}
fun primaryConstructorRecursiveVisitor(block: (KtPrimaryConstructor) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitPrimaryConstructor(primaryConstructor: KtPrimaryConstructor) {
super.visitPrimaryConstructor(primaryConstructor)
block(primaryConstructor)
}
}
fun destructuringDeclarationVisitor(block: (KtDestructuringDeclaration) -> Unit) =
object : KtVisitorVoid() {
override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
block(destructuringDeclaration)
}
}
fun destructuringDeclarationRecursiveVisitor(block: (KtDestructuringDeclaration) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
super.visitDestructuringDeclaration(destructuringDeclaration)
block(destructuringDeclaration)
}
}
fun dotQualifiedExpressionVisitor(block: (KtDotQualifiedExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(dotQualifiedExpression: KtDotQualifiedExpression) {
block(dotQualifiedExpression)
}
}
fun dotQualifiedExpressionRecursiveVisitor(block: (KtDotQualifiedExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitDotQualifiedExpression(dotQualifiedExpression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(dotQualifiedExpression)
block(dotQualifiedExpression)
}
}
fun prefixExpressionVisitor(block: (KtPrefixExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitPrefixExpression(prefixExpression: KtPrefixExpression) {
block(prefixExpression)
}
}
fun prefixExpressionRecursiveVisitor(block: (KtPrefixExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitPrefixExpression(prefixExpression: KtPrefixExpression) {
super.visitPrefixExpression(prefixExpression)
block(prefixExpression)
}
}
fun namedFunctionVisitor(block: (KtNamedFunction) -> Unit) =
object : KtVisitorVoid() {
override fun visitNamedFunction(namedFunction: KtNamedFunction) {
block(namedFunction)
}
}
fun namedFunctionRecursiveVisitor(block: (KtNamedFunction) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitNamedFunction(namedFunction: KtNamedFunction) {
super.visitNamedFunction(namedFunction)
block(namedFunction)
}
}
fun annotationEntryVisitor(block: (KtAnnotationEntry) -> Unit) =
object : KtVisitorVoid() {
override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
block(annotationEntry)
}
}
fun annotationEntryRecursiveVisitor(block: (KtAnnotationEntry) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
super.visitAnnotationEntry(annotationEntry)
block(annotationEntry)
}
}
fun lambdaExpressionVisitor(block: (KtLambdaExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
block(lambdaExpression)
}
}
fun lambdaExpressionRecursiveVisitor(block: (KtLambdaExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
super.visitLambdaExpression(lambdaExpression)
block(lambdaExpression)
}
}
fun enumEntryVisitor(block: (KtEnumEntry) -> Unit) =
object : KtVisitorVoid() {
override fun visitEnumEntry(enumEntry: KtEnumEntry) {
block(enumEntry)
}
}
fun enumEntryRecursiveVisitor(block: (KtEnumEntry) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitEnumEntry(enumEntry: KtEnumEntry) {
super.visitEnumEntry(enumEntry)
block(enumEntry)
}
}
fun packageDirectiveVisitor(block: (KtPackageDirective) -> Unit) =
object : KtVisitorVoid() {
override fun visitPackageDirective(packageDirective: KtPackageDirective) {
block(packageDirective)
}
}
fun packageDirectiveRecursiveVisitor(block: (KtPackageDirective) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitPackageDirective(packageDirective: KtPackageDirective) {
super.visitPackageDirective(packageDirective)
block(packageDirective)
}
}
fun binaryExpressionVisitor(block: (KtBinaryExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitBinaryExpression(binaryExpression: KtBinaryExpression) {
block(binaryExpression)
}
}
fun binaryExpressionRecursiveVisitor(block: (KtBinaryExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitBinaryExpression(binaryExpression: KtBinaryExpression) {
super.visitBinaryExpression(binaryExpression)
block(binaryExpression)
}
}
fun declarationVisitor(block: (KtDeclaration) -> Unit) =
object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
block(declaration)
}
}
fun declarationRecursiveVisitor(block: (KtDeclaration) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
super.visitDeclaration(declaration)
block(declaration)
}
}
fun simpleNameExpressionVisitor(block: (KtSimpleNameExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitSimpleNameExpression(simpleNameExpression: KtSimpleNameExpression) {
block(simpleNameExpression)
}
}
fun simpleNameExpressionRecursiveVisitor(block: (KtSimpleNameExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitSimpleNameExpression(simpleNameExpression: KtSimpleNameExpression) {
super.visitSimpleNameExpression(simpleNameExpression)
block(simpleNameExpression)
}
}
fun propertyAccessorVisitor(block: (KtPropertyAccessor) -> Unit) =
object : KtVisitorVoid() {
override fun visitPropertyAccessor(propertyAccessor: KtPropertyAccessor) {
block(propertyAccessor)
}
}
fun propertyAccessorRecursiveVisitor(block: (KtPropertyAccessor) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitPropertyAccessor(propertyAccessor: KtPropertyAccessor) {
super.visitPropertyAccessor(propertyAccessor)
block(propertyAccessor)
}
}
fun referenceExpressionVisitor(block: (KtReferenceExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitReferenceExpression(referenceExpression: KtReferenceExpression) {
block(referenceExpression)
}
}
fun referenceExpressionRecursiveVisitor(block: (KtReferenceExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitReferenceExpression(referenceExpression: KtReferenceExpression) {
super.visitReferenceExpression(referenceExpression)
block(referenceExpression)
}
}
fun valueArgumentVisitor(block: (KtValueArgument) -> Unit) =
object : KtVisitorVoid() {
override fun visitArgument(valueArgument: KtValueArgument) {
block(valueArgument)
}
}
fun valueArgumentRecursiveVisitor(block: (KtValueArgument) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitArgument(valueArgument: KtValueArgument) {
super.visitArgument(valueArgument)
block(valueArgument)
}
}
fun whenExpressionVisitor(block: (KtWhenExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitWhenExpression(whenExpression: KtWhenExpression) {
block(whenExpression)
}
}
fun whenExpressionRecursiveVisitor(block: (KtWhenExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitWhenExpression(whenExpression: KtWhenExpression) {
super.visitWhenExpression(whenExpression)
block(whenExpression)
}
}
fun modifierListVisitor(block: (KtModifierList) -> Unit) =
object : KtVisitorVoid() {
override fun visitModifierList(modifierList: KtModifierList) {
block(modifierList)
}
}
fun modifierListRecursiveVisitor(block: (KtModifierList) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitModifierList(modifierList: KtModifierList) {
super.visitModifierList(modifierList)
block(modifierList)
}
}
fun namedDeclarationVisitor(block: (KtNamedDeclaration) -> Unit) =
object : KtVisitorVoid() {
override fun visitNamedDeclaration(namedDeclaration: KtNamedDeclaration) {
block(namedDeclaration)
}
}
fun namedDeclarationRecursiveVisitor(block: (KtNamedDeclaration) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitNamedDeclaration(namedDeclaration: KtNamedDeclaration) {
super.visitNamedDeclaration(namedDeclaration)
block(namedDeclaration)
}
}
fun qualifiedExpressionVisitor(block: (KtQualifiedExpression) -> Unit) =
object : KtVisitorVoid() {
override fun visitQualifiedExpression(qualifiedExpression: KtQualifiedExpression) {
block(qualifiedExpression)
}
}
fun qualifiedExpressionRecursiveVisitor(block: (KtQualifiedExpression) -> Unit) =
object : KtTreeVisitorVoid() {
override fun visitQualifiedExpression(qualifiedExpression: KtQualifiedExpression) {
super.visitQualifiedExpression(qualifiedExpression)
block(qualifiedExpression)
}
}
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -23,7 +12,7 @@ import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.expressionVisitor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -34,17 +23,13 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
abstract class AbstractPrimitiveRangeToInspection : AbstractKotlinInspection() { abstract class AbstractPrimitiveRangeToInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return expressionVisitor { expression ->
override fun visitExpression(expression: KtExpression) { if (expression !is KtBinaryExpression && expression !is KtDotQualifiedExpression) return@expressionVisitor
super.visitExpression(expression)
if (expression !is KtBinaryExpression && expression !is KtDotQualifiedExpression) return val fqName = expression.getCallableDescriptor()?.fqNameUnsafe?.asString() ?: return@expressionVisitor
if (!fqName.matches(REGEX_RANGE_TO)) return@expressionVisitor
val fqName = expression.getCallableDescriptor()?.fqNameUnsafe?.asString() ?: return visitRangeToExpression(expression, holder)
if (!fqName.matches(REGEX_RANGE_TO)) return
visitRangeToExpression(expression, holder)
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -60,43 +49,41 @@ class AddVarianceModifierInspection : AbstractKotlinInspection() {
} }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return classOrObjectVisitor { klass ->
val context = klass.analyzeFully()
private fun variancePossible( for (typeParameter in klass.typeParameters) {
klass: KtClassOrObject, if (typeParameter.variance != Variance.INVARIANT) continue
parameterDescriptor: TypeParameterDescriptor, val parameterDescriptor =
variance: Variance, context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeParameter) as? TypeParameterDescriptor ?: continue
context: BindingContext val variances = listOf(Variance.IN_VARIANCE, Variance.OUT_VARIANCE).filter {
) = VarianceCheckerCore( variancePossible(klass, parameterDescriptor, it, context)
context, }
DiagnosticSink.DO_NOTHING, if (variances.size == 1) {
ManualVariance(parameterDescriptor, variance) val suggested = variances.first()
).checkClassOrObject(klass) val fixes = variances.map(::AddVarianceFix)
holder.registerProblem(
override fun visitClassOrObject(klass: KtClassOrObject) { typeParameter,
val context = klass.analyzeFully() "Type parameter can have $suggested variance",
for (typeParameter in klass.typeParameters) { ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
if (typeParameter.variance != Variance.INVARIANT) continue *fixes.toTypedArray()
val parameterDescriptor = )
context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeParameter) as? TypeParameterDescriptor ?: continue
val variances = listOf(Variance.IN_VARIANCE, Variance.OUT_VARIANCE).filter {
variancePossible(klass, parameterDescriptor, it, context)
}
if (variances.size == 1) {
val suggested = variances.first()
val fixes = variances.map(::AddVarianceFix)
holder.registerProblem(
typeParameter,
"Type parameter can have $suggested variance",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
*fixes.toTypedArray()
)
}
} }
} }
} }
} }
private fun variancePossible(
klass: KtClassOrObject,
parameterDescriptor: TypeParameterDescriptor,
variance: Variance,
context: BindingContext
) = VarianceCheckerCore(
context,
DiagnosticSink.DO_NOTHING,
ManualVariance(parameterDescriptor, variance)
).checkClassOrObject(klass)
class AddVarianceFix(val variance: Variance) : LocalQuickFix { class AddVarianceFix(val variance: Variance) : LocalQuickFix {
override fun getName() = "Add '$variance' variance" override fun getName() = "Add '$variance' variance"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -26,7 +15,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.classVisitor
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -34,47 +23,45 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
class ArrayInDataClassInspection : AbstractKotlinInspection() { class ArrayInDataClassInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return classVisitor { klass ->
override fun visitClass(klass: KtClass) { if (!klass.isData()) return@classVisitor
if (!klass.isData()) return val constructor = klass.primaryConstructor ?: return@classVisitor
val constructor = klass.primaryConstructor ?: return if (hasOverriddenEqualsAndHashCode(klass)) return@classVisitor
if (hasOverriddenEqualsAndHashCode(klass)) return val context = constructor.analyze(BodyResolveMode.PARTIAL)
val context = constructor.analyze(BodyResolveMode.PARTIAL) for (parameter in constructor.valueParameters) {
for (parameter in constructor.valueParameters) { if (!parameter.hasValOrVar()) continue
if (!parameter.hasValOrVar()) continue val type = context.get(BindingContext.TYPE, parameter.typeReference) ?: continue
val type = context.get(BindingContext.TYPE, parameter.typeReference) ?: continue if (KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) {
if (KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) { holder.registerProblem(parameter,
holder.registerProblem(parameter, "Array property in data class: it's recommended to override equals() / hashCode()",
"Array property in data class: it's recommended to override equals() / hashCode()", ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, GenerateEqualsAndHashcodeFix())
GenerateEqualsAndHashcodeFix())
}
} }
} }
private fun hasOverriddenEqualsAndHashCode(klass: KtClass): Boolean {
var overriddenEquals = false
var overriddenHashCode = false
for (declaration in klass.declarations) {
if (declaration !is KtFunction) continue
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) continue
if (declaration.nameAsName == OperatorNameConventions.EQUALS && declaration.valueParameters.size == 1) {
val parameter = declaration.valueParameters.single()
val context = declaration.analyze(BodyResolveMode.PARTIAL)
val type = context.get(BindingContext.TYPE, parameter.typeReference)
if (type != null && KotlinBuiltIns.isNullableAny(type)) {
overriddenEquals = true
}
}
if (declaration.name == "hashCode" && declaration.valueParameters.size == 0) {
overriddenHashCode = true
}
}
return overriddenEquals && overriddenHashCode
}
} }
} }
private fun hasOverriddenEqualsAndHashCode(klass: KtClass): Boolean {
var overriddenEquals = false
var overriddenHashCode = false
for (declaration in klass.declarations) {
if (declaration !is KtFunction) continue
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) continue
if (declaration.nameAsName == OperatorNameConventions.EQUALS && declaration.valueParameters.size == 1) {
val parameter = declaration.valueParameters.single()
val context = declaration.analyze(BodyResolveMode.PARTIAL)
val type = context.get(BindingContext.TYPE, parameter.typeReference)
if (type != null && KotlinBuiltIns.isNullableAny(type)) {
overriddenEquals = true
}
}
if (declaration.name == "hashCode" && declaration.valueParameters.size == 0) {
overriddenHashCode = true
}
}
return overriddenEquals && overriddenHashCode
}
class GenerateEqualsAndHashcodeFix : LocalQuickFix { class GenerateEqualsAndHashcodeFix : LocalQuickFix {
override fun getName() = "Generate equals() and hashCode()" override fun getName() = "Generate equals() and hashCode()"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -78,46 +67,43 @@ class CanBeParameterInspection : AbstractKotlinInspection() {
} }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return parameterVisitor(fun(parameter) {
// Applicable to val / var parameters of a class / object primary constructors
val valOrVar = parameter.valOrVarKeyword ?: return
val name = parameter.name ?: return
if (parameter.hasModifier(OVERRIDE_KEYWORD)) return
if (parameter.annotationEntries.isNotEmpty()) return
val constructor = parameter.parent.parent as? KtPrimaryConstructor ?: return
val klass = constructor.getContainingClassOrObject() as? KtClass ?: return
if (klass.isData()) return
override fun visitParameter(parameter: KtParameter) { val useScope = parameter.useScope
// Applicable to val / var parameters of a class / object primary constructors val restrictedScope = if (useScope is GlobalSearchScope) {
val valOrVar = parameter.valOrVarKeyword ?: return val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(parameter.project)
val name = parameter.name ?: return for (accessorName in parameter.getAccessorNames()) {
if (parameter.hasModifier(OVERRIDE_KEYWORD)) return when (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(accessorName, useScope, null, null)) {
if (parameter.annotationEntries.isNotEmpty()) return ZERO_OCCURRENCES -> {
val constructor = parameter.parent.parent as? KtPrimaryConstructor ?: return } // go on
val klass = constructor.getContainingClassOrObject() as? KtClass ?: return else -> return // accessor in use: should remain a property
if (klass.isData()) return
val useScope = parameter.useScope
val restrictedScope = if (useScope is GlobalSearchScope) {
val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(parameter.project)
for (accessorName in parameter.getAccessorNames()) {
when (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(accessorName, useScope, null, null)) {
ZERO_OCCURRENCES -> {
} // go on
else -> return // accessor in use: should remain a property
}
} }
// TOO_MANY_OCCURRENCES: too expensive
// ZERO_OCCURRENCES: unused at all, reported elsewhere
if (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(name, useScope, null, null) != FEW_OCCURRENCES) return
KotlinSourceFilterScope.projectSources(useScope, parameter.project)
} }
else useScope // TOO_MANY_OCCURRENCES: too expensive
// Find all references and check them // ZERO_OCCURRENCES: unused at all, reported elsewhere
val references = ReferencesSearch.search(parameter, restrictedScope) if (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(name, useScope, null, null) != FEW_OCCURRENCES) return
if (references.none()) return KotlinSourceFilterScope.projectSources(useScope, parameter.project)
if (references.any { it.usedAsPropertyIn(klass) }) return
holder.registerProblem(
valOrVar,
"Constructor parameter is never used as a property",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveValVarFix(parameter)
)
} }
} else useScope
// Find all references and check them
val references = ReferencesSearch.search(parameter, restrictedScope)
if (references.none()) return
if (references.any { it.usedAsPropertyIn(klass) }) return
holder.registerProblem(
valOrVar,
"Constructor parameter is never used as a property",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveValVarFix(parameter)
)
})
} }
class RemoveValVarFix(val parameter: KtParameter) : LocalQuickFix { class RemoveValVarFix(val parameter: KtParameter) : LocalQuickFix {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -25,9 +14,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention import org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.propertyVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
@@ -35,39 +23,37 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class CanBePrimaryConstructorPropertyInspection : AbstractKotlinInspection() { class CanBePrimaryConstructorPropertyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return propertyVisitor(fun(property) {
override fun visitProperty(property: KtProperty) { if (property.isLocal) return
if (property.isLocal) return if (property.getter != null || property.setter != null || property.delegate != null) return
if (property.getter != null || property.setter != null || property.delegate != null) return val assigned = property.initializer as? KtReferenceExpression ?: return
val assigned = property.initializer as? KtReferenceExpression ?: return
val context = property.analyzeFully() val context = property.analyzeFully()
val assignedDescriptor = context.get(BindingContext.REFERENCE_TARGET, assigned) as? ValueParameterDescriptor ?: return val assignedDescriptor = context.get(BindingContext.REFERENCE_TARGET, assigned) as? ValueParameterDescriptor ?: return
val containingConstructor = assignedDescriptor.containingDeclaration as? ClassConstructorDescriptor ?: return val containingConstructor = assignedDescriptor.containingDeclaration as? ClassConstructorDescriptor ?: return
if (containingConstructor.containingDeclaration.isData) return if (containingConstructor.containingDeclaration.isData) return
val propertyTypeReference = property.typeReference val propertyTypeReference = property.typeReference
val propertyType = context.get(BindingContext.TYPE, propertyTypeReference) val propertyType = context.get(BindingContext.TYPE, propertyTypeReference)
if (propertyType != null && propertyType != assignedDescriptor.type) return if (propertyType != null && propertyType != assignedDescriptor.type) return
val nameIdentifier = property.nameIdentifier ?: return val nameIdentifier = property.nameIdentifier ?: return
if (nameIdentifier.text != assignedDescriptor.name.asString()) return if (nameIdentifier.text != assignedDescriptor.name.asString()) return
val assignedParameter = DescriptorToSourceUtils.descriptorToDeclaration(assignedDescriptor) as? KtParameter ?: return val assignedParameter = DescriptorToSourceUtils.descriptorToDeclaration(assignedDescriptor) as? KtParameter ?: return
if (property.containingClassOrObject !== assignedParameter.containingClassOrObject) return if (property.containingClassOrObject !== assignedParameter.containingClassOrObject) return
if (property.containingClassOrObject?.isInterfaceClass() ?: false) return if (property.containingClassOrObject?.isInterfaceClass() ?: false) return
holder.registerProblem(holder.manager.createProblemDescriptor( holder.registerProblem(holder.manager.createProblemDescriptor(
nameIdentifier, nameIdentifier,
nameIdentifier, nameIdentifier,
"Property is explicitly assigned to parameter ${assignedDescriptor.name}, can be declared directly in constructor", "Property is explicitly assigned to parameter ${assignedDescriptor.name}, can be declared directly in constructor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly, isOnTheFly,
MovePropertyToConstructorIntention() MovePropertyToConstructorIntention()
)) ))
} })
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -21,8 +10,8 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfToWhenIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfToWhenIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isOneLiner
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isOneLiner
import org.jetbrains.kotlin.idea.intentions.branches import org.jetbrains.kotlin.idea.intentions.branches
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -31,45 +20,41 @@ import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
class CascadeIfInspection : AbstractKotlinInspection() { class CascadeIfInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { ifExpressionVisitor(fun(expression) {
override fun visitIfExpression(expression: KtIfExpression) { val branches = expression.branches
super.visitIfExpression(expression) if (branches.size <= 2) return
if (expression.isOneLiner()) return
val branches = expression.branches if (branches.any {
if (branches.size <= 2) return it == null ||
if (expression.isOneLiner()) return it.lastBlockStatementOrThis() is KtIfExpression
}) return
if (branches.any { if (expression.isElseIf()) return
it == null ||
it.lastBlockStatementOrThis() is KtIfExpression
}) return
if (expression.isElseIf()) return if (expression.anyDescendantOfType<KtExpressionWithLabel> {
it is KtBreakExpression || it is KtContinueExpression
}) return
if (expression.anyDescendantOfType<KtExpressionWithLabel> { var current: KtIfExpression? = expression
it is KtBreakExpression || it is KtContinueExpression while (current != null) {
}) return val condition = current.condition
when (condition) {
var current: KtIfExpression? = expression is KtBinaryExpression -> when (condition.operationToken) {
while (current != null) { KtTokens.ANDAND, KtTokens.OROR -> return
val condition = current.condition }
when (condition) { is KtUnaryExpression -> when (condition.operationToken) {
is KtBinaryExpression -> when (condition.operationToken) { KtTokens.EXCL -> return
KtTokens.ANDAND, KtTokens.OROR -> return
}
is KtUnaryExpression -> when (condition.operationToken) {
KtTokens.EXCL -> return
}
}
current = current.`else` as? KtIfExpression
} }
holder.registerProblem(
expression.ifKeyword,
"Cascade if should be replaced with when",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(IfToWhenIntention(), expression.containingKtFile)
)
} }
current = current.`else` as? KtIfExpression
} }
holder.registerProblem(
expression.ifKeyword,
"Cascade if should be replaced with when",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(IfToWhenIntention(), expression.containingKtFile)
)
})
} }
@@ -1,24 +1,16 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.FileModificationService import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.codeInspection.* import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
@@ -34,8 +26,8 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.targetDescriptors import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
@@ -63,33 +55,29 @@ class ConflictingExtensionPropertyInspection : AbstractKotlinInspection() {
val file = session.file as? KtFile ?: return PsiElementVisitor.EMPTY_VISITOR val file = session.file as? KtFile ?: return PsiElementVisitor.EMPTY_VISITOR
val resolutionFacade = file.getResolutionFacade() val resolutionFacade = file.getResolutionFacade()
return object : KtVisitorVoid() { return propertyVisitor(fun(property: KtProperty) {
override fun visitProperty(property: KtProperty) { if (property.receiverTypeReference != null) {
super.visitProperty(property) val nameElement = property.nameIdentifier ?: return
val propertyDescriptor = property.resolveToDescriptorIfAny() as? PropertyDescriptor ?: return
if (property.receiverTypeReference != null) { val syntheticScopes = resolutionFacade.frontendService<SyntheticScopes>()
val nameElement = property.nameIdentifier ?: return val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return
val propertyDescriptor = property.resolveToDescriptorIfAny() as? PropertyDescriptor ?: return
val syntheticScopes = resolutionFacade.frontendService<SyntheticScopes>() // don't report on hidden declarations
val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(propertyDescriptor)) return
// don't report on hidden declarations val fixes = createFixes(property, conflictingExtension, isOnTheFly)
if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(propertyDescriptor)) return
val fixes = createFixes(property, conflictingExtension, isOnTheFly) val problemDescriptor = holder.manager.createProblemDescriptor(
nameElement,
val problemDescriptor = holder.manager.createProblemDescriptor( "This property conflicts with synthetic extension and should be removed or renamed to avoid breaking code by future changes in the compiler",
nameElement, true,
"This property conflicts with synthetic extension and should be removed or renamed to avoid breaking code by future changes in the compiler", fixes,
true, ProblemHighlightType.GENERIC_ERROR_OR_WARNING
fixes, )
ProblemHighlightType.GENERIC_ERROR_OR_WARNING holder.registerProblem(problemDescriptor)
)
holder.registerProblem(problemDescriptor)
}
} }
} })
} }
private fun conflictingSyntheticExtension(descriptor: PropertyDescriptor, scopes: SyntheticScopes): SyntheticJavaPropertyDescriptor? { private fun conflictingSyntheticExtension(descriptor: PropertyDescriptor, scopes: SyntheticScopes): SyntheticJavaPropertyDescriptor? {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -37,31 +26,27 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class ConstantConditionIfInspection : AbstractKotlinInspection() { class ConstantConditionIfInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return ifExpressionVisitor(fun(expression) {
override fun visitIfExpression(expression: KtIfExpression) { val condition = expression.condition ?: return
super.visitIfExpression(expression)
val condition = expression.condition ?: return val context = condition.analyze(BodyResolveMode.PARTIAL)
val constantValue = condition.constantBooleanValue(context) ?: return
val context = condition.analyze(BodyResolveMode.PARTIAL) val fixes = mutableListOf<LocalQuickFix>()
val constantValue = condition.constantBooleanValue(context) ?: return
val fixes = mutableListOf<LocalQuickFix>() if (expression.branch(constantValue) != null) {
val keepBraces = expression.isElseIf() && expression.branch(constantValue) is KtBlockExpression
if (expression.branch(constantValue) != null) { fixes += SimplifyFix(constantValue, expression.isUsedAsExpression(context), keepBraces)
val keepBraces = expression.isElseIf() && expression.branch(constantValue) is KtBlockExpression
fixes += SimplifyFix(constantValue, expression.isUsedAsExpression(context), keepBraces)
}
if (!constantValue && expression.`else` == null) {
fixes += RemoveFix()
}
holder.registerProblem(condition,
"Condition is always '$constantValue'",
*fixes.toTypedArray())
} }
}
if (!constantValue && expression.`else` == null) {
fixes += RemoveFix()
}
holder.registerProblem(condition,
"Condition is always '$constantValue'",
*fixes.toTypedArray())
})
} }
private class SimplifyFix( private class SimplifyFix(
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -23,9 +12,8 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention import org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.callExpressionVisitor
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
@@ -35,29 +23,25 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class CopyWithoutNamedArgumentsInspection : AbstractKotlinInspection() { class CopyWithoutNamedArgumentsInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return callExpressionVisitor(fun(expression) {
override fun visitCallExpression(expression: KtCallExpression) { val reference = expression.referenceExpression() as? KtNameReferenceExpression ?: return
super.visitCallExpression(expression) if (reference.getReferencedNameAsName() != DataClassDescriptorResolver.COPY_METHOD_NAME) return
if (expression.valueArguments.all { it.isNamed() }) return
val reference = expression.referenceExpression() as? KtNameReferenceExpression ?: return val context = expression.analyze(BodyResolveMode.PARTIAL)
if (reference.getReferencedNameAsName() != DataClassDescriptorResolver.COPY_METHOD_NAME) return val call = expression.getResolvedCall(context) ?: return
if (expression.valueArguments.all { it.isNamed() }) return val receiver = call.dispatchReceiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL) if (!receiver.isData) return
val call = expression.getResolvedCall(context) ?: return if (call.candidateDescriptor != context[BindingContext.DATA_CLASS_COPY_FUNCTION, receiver]) return
val receiver = call.dispatchReceiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor ?: return
if (!receiver.isData) return holder.registerProblem(
if (call.candidateDescriptor != context[BindingContext.DATA_CLASS_COPY_FUNCTION, receiver]) return expression.calleeExpression ?: return,
"'copy' method of data class is called without named arguments",
holder.registerProblem( ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
expression.calleeExpression ?: return, IntentionWrapper(AddNamesToCallArgumentsIntention(), expression.containingKtFile)
"'copy' method of data class is called without named arguments", )
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, })
IntentionWrapper(AddNamesToCallArgumentsIntention(), expression.containingKtFile)
)
}
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -20,29 +9,24 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtPrimaryConstructor import org.jetbrains.kotlin.psi.primaryConstructorVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.containingClass import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.isPrivate import org.jetbrains.kotlin.psi.psiUtil.isPrivate
class DataClassPrivateConstructorInspection : AbstractKotlinInspection() { class DataClassPrivateConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return primaryConstructorVisitor { constructor ->
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) { if (constructor.containingClass()?.isData() == true && constructor.isPrivate()) {
super.visitPrimaryConstructor(constructor) val keyword = constructor.modifierList?.getModifier(KtTokens.PRIVATE_KEYWORD) ?: return@primaryConstructorVisitor
val problemDescriptor = holder.manager.createProblemDescriptor(
keyword,
keyword,
"Private data class constructor is exposed via the generated 'copy' method.",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly
)
if (constructor.containingClass()?.isData() == true && constructor.isPrivate()) { holder.registerProblem(problemDescriptor)
val keyword = constructor.modifierList?.getModifier(KtTokens.PRIVATE_KEYWORD) ?: return
val problemDescriptor = holder.manager.createProblemDescriptor(
keyword,
keyword,
"Private data class constructor is exposed via the generated 'copy' method.",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly
)
holder.registerProblem(problemDescriptor)
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -21,42 +10,37 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.destructuringDeclarationVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
class DestructuringWrongNameInspection : AbstractKotlinInspection() { class DestructuringWrongNameInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return destructuringDeclarationVisitor(fun(destructuringDeclaration) {
override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) { val initializer = destructuringDeclaration.initializer ?: return
super.visitDestructuringDeclaration(destructuringDeclaration) val type = initializer.analyze().getType(initializer) ?: return
val initializer = destructuringDeclaration.initializer ?: return val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return
val type = initializer.analyze().getType(initializer) ?: return
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return val primaryParameterNames = classDescriptor.constructors
.firstOrNull { it.isPrimary }
?.valueParameters
?.map { it.name.asString() } ?: return
val primaryParameterNames = classDescriptor.constructors destructuringDeclaration.entries.forEachIndexed { entryIndex, entry ->
.firstOrNull { it.isPrimary } val variableName = entry.name
?.valueParameters if (variableName != primaryParameterNames.getOrNull(entryIndex)) {
?.map { it.name.asString() } ?: return for ((parameterIndex, parameterName) in primaryParameterNames.withIndex()) {
if (parameterIndex == entryIndex) continue
destructuringDeclaration.entries.forEachIndexed { entryIndex, entry -> if (variableName == parameterName) {
val variableName = entry.name val fix = primaryParameterNames.getOrNull(entryIndex)?.let { RenameElementFix(entry, it) }
if (variableName != primaryParameterNames.getOrNull(entryIndex)) { holder.registerProblem(
for ((parameterIndex, parameterName) in primaryParameterNames.withIndex()) { entry,
if (parameterIndex == entryIndex) continue "Variable name '$variableName' matches the name of a different component",
if (variableName == parameterName) { *listOfNotNull(fix).toTypedArray())
val fix = primaryParameterNames.getOrNull(entryIndex)?.let { RenameElementFix(entry, it) } break
holder.registerProblem(
entry,
"Variable name '$variableName' matches the name of a different component",
*listOfNotNull(fix).toTypedArray())
break
}
} }
} }
} }
} }
} })
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -29,10 +18,7 @@ import org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateEqualsAndHashcod
import org.jetbrains.kotlin.idea.actions.generate.findDeclaredEquals import org.jetbrains.kotlin.idea.actions.generate.findDeclaredEquals
import org.jetbrains.kotlin.idea.actions.generate.findDeclaredHashCode import org.jetbrains.kotlin.idea.actions.generate.findDeclaredHashCode
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
@@ -71,35 +57,33 @@ sealed class GenerateEqualsOrHashCodeFix : LocalQuickFix {
class EqualsOrHashCodeInspection : AbstractKotlinInspection() { class EqualsOrHashCodeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object: KtVisitorVoid() { return classOrObjectVisitor(fun(classOrObject) {
override fun visitClassOrObject(classOrObject: KtClassOrObject) { val nameIdentifier = classOrObject.nameIdentifier ?: return
val nameIdentifier = classOrObject.nameIdentifier ?: return val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return val hasEquals = classDescriptor.findDeclaredEquals(false) != null
val hasEquals = classDescriptor.findDeclaredEquals(false) != null val hasHashCode = classDescriptor.findDeclaredHashCode(false) != null
val hasHashCode = classDescriptor.findDeclaredHashCode(false) != null if (!hasEquals && !hasHashCode) return
if (!hasEquals && !hasHashCode) return
when (classDescriptor.kind) { when (classDescriptor.kind) {
ClassKind.OBJECT -> { ClassKind.OBJECT -> {
if (classOrObject.superTypeListEntries.isNotEmpty()) return if (classOrObject.superTypeListEntries.isNotEmpty()) return
holder.registerProblem(nameIdentifier, "equals()/hashCode() in object declaration", DeleteEqualsAndHashCodeFix) holder.registerProblem(nameIdentifier, "equals()/hashCode() in object declaration", DeleteEqualsAndHashCodeFix)
}
ClassKind.CLASS -> {
if (hasEquals && hasHashCode) return
val description = InspectionsBundle.message(
"inspection.equals.hashcode.only.one.defined.problem.descriptor",
if (hasEquals) "<code>equals()</code>" else "<code>hashCode()</code>",
if (hasEquals) "<code>hashCode()</code>" else "<code>equals()</code>"
)
holder.registerProblem(
nameIdentifier,
description,
if (hasEquals) GenerateEqualsOrHashCodeFix.HashCode else GenerateEqualsOrHashCodeFix.Equals
)
}
else -> return
} }
ClassKind.CLASS -> {
if (hasEquals && hasHashCode) return
val description = InspectionsBundle.message(
"inspection.equals.hashcode.only.one.defined.problem.descriptor",
if (hasEquals) "<code>equals()</code>" else "<code>hashCode()</code>",
if (hasEquals) "<code>hashCode()</code>" else "<code>equals()</code>"
)
holder.registerProblem(
nameIdentifier,
description,
if (hasEquals) GenerateEqualsOrHashCodeFix.HashCode else GenerateEqualsOrHashCodeFix.Equals
)
}
else -> return
} }
} })
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -38,29 +27,25 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() { class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return dotQualifiedExpressionVisitor(fun(expression) {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { val callExpression = expression.callExpression ?: return
super.visitDotQualifiedExpression(expression) val args = callExpression.valueArguments
val firstArg = args.firstOrNull() ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL)
if (KotlinBuiltIns.FQ_NAMES.mutableList !=
firstArg.getArgumentExpression()?.getType(context)?.constructor?.declarationDescriptor?.fqNameSafe) return
val callExpression = expression.callExpression ?: return val resolvedCall = expression.getResolvedCall(context) ?: return
val args = callExpression.valueArguments val descriptor = resolvedCall.resultingDescriptor as? JavaMethodDescriptor ?: return
val firstArg = args.firstOrNull() ?: return val fqName = descriptor.importableFqName?.asString() ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL) if (!canReplaceWithStdLib(expression, fqName, args)) return
if (KotlinBuiltIns.FQ_NAMES.mutableList !=
firstArg.getArgumentExpression()?.getType(context)?.constructor?.declarationDescriptor?.fqNameSafe) return
val resolvedCall = expression.getResolvedCall(context) ?: return val methodName = fqName.split(".").last()
val descriptor = resolvedCall.resultingDescriptor as? JavaMethodDescriptor ?: return holder.registerProblem(expression,
val fqName = descriptor.importableFqName?.asString() ?: return "Java Collections static method call should be replaced with Kotlin stdlib",
if (!canReplaceWithStdLib(expression, fqName, args)) return ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithStdLibFix(methodName, firstArg.text))
val methodName = fqName.split(".").last() })
holder.registerProblem(expression,
"Java Collections static method call should be replaced with Kotlin stdlib",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithStdLibFix(methodName, firstArg.text))
}
}
} }
private fun canReplaceWithStdLib(expression: KtDotQualifiedExpression, fqName: String, args: List<KtValueArgument>): Boolean { private fun canReplaceWithStdLib(expression: KtDotQualifiedExpression, fqName: String, args: List<KtValueArgument>): Boolean {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -23,30 +12,28 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtParenthesizedExpression
import org.jetbrains.kotlin.psi.KtPrefixExpression import org.jetbrains.kotlin.psi.KtPrefixExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.prefixExpressionVisitor
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.types.typeUtil.isBoolean import org.jetbrains.kotlin.types.typeUtil.isBoolean
class KotlinDoubleNegationInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class KotlinDoubleNegationInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { prefixExpressionVisitor(fun(expression) {
override fun visitPrefixExpression(expression: KtPrefixExpression) { if (expression.operationToken != KtTokens.EXCL ||
if (expression.operationToken != KtTokens.EXCL || expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true) {
expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true) { return
return
}
var parent = expression.parent
while (parent is KtParenthesizedExpression) {
parent = parent.parent
}
if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) {
holder.registerProblem(expression,
"Redundant double negation",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
DoubleNegationFix())
}
} }
} var parent = expression.parent
while (parent is KtParenthesizedExpression) {
parent = parent.parent
}
if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) {
holder.registerProblem(expression,
"Redundant double negation",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
DoubleNegationFix())
}
})
private class DoubleNegationFix : LocalQuickFix { private class DoubleNegationFix : LocalQuickFix {
override fun getName() = "Remove redundant negations" override fun getName() = "Remove redundant negations"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -29,52 +18,49 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { namedFunctionVisitor(fun(function) {
override fun visitNamedFunction(function: KtNamedFunction) { val funKeyword = function.funKeyword ?: return
super.visitNamedFunction(function) val modifierList = function.modifierList ?: return
val funKeyword = function.funKeyword ?: return if (!modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
val modifierList = function.modifierList ?: return if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return
if (!modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return if (function.annotationEntries.isNotEmpty()) return
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return if (function.containingClass()?.isData() == true) return
if (function.annotationEntries.isNotEmpty()) return
if (function.containingClass()?.isData() == true) return
val bodyExpression = function.bodyExpression ?: return val bodyExpression = function.bodyExpression ?: return
val qualifiedExpression = when (bodyExpression) { val qualifiedExpression = when (bodyExpression) {
is KtDotQualifiedExpression -> bodyExpression is KtDotQualifiedExpression -> bodyExpression
is KtBlockExpression -> { is KtBlockExpression -> {
val body = bodyExpression.statements.singleOrNull() val body = bodyExpression.statements.singleOrNull()
when (body) { when (body) {
is KtReturnExpression -> body.returnedExpression is KtReturnExpression -> body.returnedExpression
is KtDotQualifiedExpression -> body.takeIf { is KtDotQualifiedExpression -> body.takeIf {
function.typeReference.let { it == null || it.text == "Unit" } function.typeReference.let { it == null || it.text == "Unit" }
}
else -> null
} }
else -> null
} }
else -> null
} as? KtDotQualifiedExpression ?: return
val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return }
if (superExpression.superTypeQualifier != null) return else -> null
} as? KtDotQualifiedExpression ?: return
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return
if (!isSameFunctionName(superCallElement, function)) return if (superExpression.superTypeQualifier != null) return
if (!isSameArguments(superCallElement, function)) return
if (function.isDefinedInDelegatedSuperType(qualifiedExpression)) return
val descriptor = holder.manager.createProblemDescriptor( val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return
function, if (!isSameFunctionName(superCallElement, function)) return
TextRange(modifierList.startOffsetInParent, funKeyword.endOffset - function.startOffset), if (!isSameArguments(superCallElement, function)) return
"Redundant override", if (function.isDefinedInDelegatedSuperType(qualifiedExpression)) return
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly, val descriptor = holder.manager.createProblemDescriptor(
RedundantOverrideFix() function,
) TextRange(modifierList.startOffsetInParent, funKeyword.endOffset - function.startOffset),
holder.registerProblem(descriptor) "Redundant override",
} ProblemHighlightType.LIKE_UNUSED_SYMBOL,
} isOnTheFly,
RedundantOverrideFix()
)
holder.registerProblem(descriptor)
})
private fun isSameArguments(superCallElement: KtCallElement, function: KtNamedFunction): Boolean { private fun isSameArguments(superCallElement: KtCallElement, function: KtNamedFunction): Boolean {
val arguments = superCallElement.valueArguments val arguments = superCallElement.valueArguments
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -28,58 +17,59 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.psi.expressionVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class LeakingThisInspection : AbstractKotlinInspection() { class LeakingThisInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return expressionVisitor { expression ->
override fun visitExpression(expression: KtExpression) { val context = expression.analyzeFully()
val context = expression.analyzeFully() val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return@expressionVisitor
val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return val description = when (leakingThisDescriptor) {
val description = when (leakingThisDescriptor) { is NonFinalClass ->
is NonFinalClass -> if (expression is KtThisExpression)
if (expression is KtThisExpression) "Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}" else
else return@expressionVisitor // Not supported yet
return // Not supported yet is NonFinalProperty ->
is NonFinalProperty -> "Accessing non-final property ${leakingThisDescriptor.property.name} in constructor"
"Accessing non-final property ${leakingThisDescriptor.property.name} in constructor" is NonFinalFunction ->
is NonFinalFunction -> "Calling non-final function ${leakingThisDescriptor.function.name} in constructor"
"Calling non-final function ${leakingThisDescriptor.function.name} in constructor" else -> return@expressionVisitor // Not supported yet
else -> return // Not supported yet }
val memberDescriptorToFix = when (leakingThisDescriptor) {
is NonFinalProperty -> leakingThisDescriptor.property
is NonFinalFunction -> leakingThisDescriptor.function
else -> null
}
val memberFix = memberDescriptorToFix?.let {
if (it.modality == Modality.OPEN) {
val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration
createMakeFinalFix(modifierListOwner)
} }
val memberDescriptorToFix = when (leakingThisDescriptor) { else null
is NonFinalProperty -> leakingThisDescriptor.property }
is NonFinalFunction -> leakingThisDescriptor.function
else -> null val klass = leakingThisDescriptor.classOrObject as? KtClass
} val classFix =
val memberFix = memberDescriptorToFix?.let { if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) {
if (it.modality == Modality.OPEN) { createMakeFinalFix(klass)
val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration
createMakeFinalFix(modifierListOwner)
} }
else null else null
}
val klass = leakingThisDescriptor.classOrObject as? KtClass holder.registerProblem(
val classFix = expression, description,
if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) { when (leakingThisDescriptor) {
createMakeFinalFix(klass) is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING
} else -> WEAK_WARNING
else null },
*(arrayOf(memberFix, classFix).filterNotNull().toTypedArray())
holder.registerProblem( )
expression, description,
when (leakingThisDescriptor) {
is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING
else -> WEAK_WARNING
},
*(arrayOf(memberFix, classFix).filterNotNull().toTypedArray())
)
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -27,7 +16,7 @@ import org.jetbrains.kotlin.idea.quickfix.AddConstModifierFix
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtVisitorVoid import org.jetbrains.kotlin.psi.propertyVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.ErrorValue import org.jetbrains.kotlin.resolve.constants.ErrorValue
@@ -48,21 +37,18 @@ class MayBeConstantInspection : AbstractKotlinInspection() {
} }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return propertyVisitor { property ->
override fun visitProperty(property: KtProperty) { val status = property.getStatus()
super.visitProperty(property) when (status) {
val status = property.getStatus() NONE, JVM_FIELD_MIGHT_BE_CONST_NO_INITIALIZER,
when (status) { MIGHT_BE_CONST_ERRONEOUS, JVM_FIELD_MIGHT_BE_CONST_ERRONEOUS -> return@propertyVisitor
NONE, JVM_FIELD_MIGHT_BE_CONST_NO_INITIALIZER, MIGHT_BE_CONST, JVM_FIELD_MIGHT_BE_CONST -> {
MIGHT_BE_CONST_ERRONEOUS, JVM_FIELD_MIGHT_BE_CONST_ERRONEOUS -> return holder.registerProblem(
MIGHT_BE_CONST, JVM_FIELD_MIGHT_BE_CONST -> { property.nameIdentifier ?: property,
holder.registerProblem( if (status == JVM_FIELD_MIGHT_BE_CONST) "'const' might be used instead of '@JvmField'" else "Might be 'const'",
property.nameIdentifier ?: property, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
if (status == JVM_FIELD_MIGHT_BE_CONST) "'const' might be used instead of '@JvmField'" else "Might be 'const'", IntentionWrapper(AddConstModifierFix(property), property.containingFile)
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, )
IntentionWrapper(AddConstModifierFix(property), property.containingFile)
)
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -25,37 +14,34 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.annotationEntryVisitor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class MigrateDiagnosticSuppressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class MigrateDiagnosticSuppressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return annotationEntryVisitor(fun(annotationEntry) {
if (annotationEntry.calleeExpression?.text != "Suppress") return
val context = annotationEntry.analyze(BodyResolveMode.PARTIAL)
val descriptor = context[BindingContext.ANNOTATION, annotationEntry] ?: return
if (descriptor.fqName != KotlinBuiltIns.FQ_NAMES.suppress) return
override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) { for (argument in annotationEntry.valueArguments) {
super.visitAnnotationEntry(annotationEntry) val expression = argument.getArgumentExpression() as? KtStringTemplateExpression ?: continue
val text = expression.text
if (text.firstOrNull() != '\"' || text.lastOrNull() != '\"') continue
val newDiagnosticFactory = MIGRATION_MAP[StringUtil.unquoteString(text)] ?: continue
if (annotationEntry.calleeExpression?.text != "Suppress") return holder.registerProblem(
val context = annotationEntry.analyze(BodyResolveMode.PARTIAL) expression,
val descriptor = context[BindingContext.ANNOTATION, annotationEntry] ?: return "Diagnostic name should be replaced by the new one",
if (descriptor.fqName != KotlinBuiltIns.FQ_NAMES.suppress) return ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceDiagnosticNameFix(newDiagnosticFactory)
for (argument in annotationEntry.valueArguments) { )
val expression = argument.getArgumentExpression() as? KtStringTemplateExpression ?: continue
val text = expression.text
if (text.firstOrNull() != '\"' || text.lastOrNull() != '\"') continue
val newDiagnosticFactory = MIGRATION_MAP[StringUtil.unquoteString(text)] ?: continue
holder.registerProblem(
expression,
"Diagnostic name should be replaced by the new one",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceDiagnosticNameFix(newDiagnosticFactory)
)
}
} }
} })
} }
class ReplaceDiagnosticNameFix(private val diagnosticFactory: DiagnosticFactory<*>) : LocalQuickFix { class ReplaceDiagnosticNameFix(private val diagnosticFactory: DiagnosticFactory<*>) : LocalQuickFix {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -28,41 +17,39 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention import org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.psi.lambdaExpressionVisitor
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinInspection() { class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return lambdaExpressionVisitor(fun(lambdaExpression) {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) { val callableReference = lambdaExpression.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression
val callableReference = lambdaExpression.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression if (callableReference != null) {
if (callableReference != null) { val context = lambdaExpression.analyze()
val context = lambdaExpression.analyze() val parentResolvedCall = lambdaExpression.getParentResolvedCall(context)
val parentResolvedCall = lambdaExpression.getParentResolvedCall(context) if (parentResolvedCall != null) {
if (parentResolvedCall != null) { val originalParameterDescriptor =
val originalParameterDescriptor = parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original
parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original if (originalParameterDescriptor != null) {
if (originalParameterDescriptor != null) { val expectedType = originalParameterDescriptor.type
val expectedType = originalParameterDescriptor.type if (expectedType.isBuiltinFunctionalType) {
if (expectedType.isBuiltinFunctionalType) { val returnType = expectedType.getReturnTypeFromFunctionType()
val returnType = expectedType.getReturnTypeFromFunctionType() if (returnType.isBuiltinFunctionalTypeOrSubtype) return
if (returnType.isBuiltinFunctionalTypeOrSubtype) return
}
} }
} }
holder.registerProblem(
lambdaExpression,
"Suspicious callable reference as the only lambda element",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile)
)
} }
holder.registerProblem(
lambdaExpression,
"Suspicious callable reference as the only lambda element",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile)
)
} }
} })
} }
class MoveIntoParenthesesIntention : ConvertLambdaToReferenceIntention( class MoveIntoParenthesesIntention : ConvertLambdaToReferenceIntention(
@@ -123,11 +123,7 @@ class EnumEntryNameInspection : NamingConventionInspection(
START_UPPER, NO_BAD_CHARACTERS_OR_UNDERSCORE START_UPPER, NO_BAD_CHARACTERS_OR_UNDERSCORE
) { ) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return enumEntryVisitor { enumEntry -> verifyName(enumEntry, holder) }
override fun visitEnumEntry(enumEntry: KtEnumEntry) {
verifyName(enumEntry, holder)
}
}
} }
} }
@@ -137,11 +133,8 @@ class FunctionNameInspection : NamingConventionInspection(
START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS
) { ) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return namedFunctionVisitor { function ->
override fun visitNamedFunction(function: KtNamedFunction) { if (!TestUtils.isInTestSourceContent(function)) {
if (TestUtils.isInTestSourceContent(function)) {
return
}
verifyName(function, holder) verifyName(function, holder)
} }
} }
@@ -154,16 +147,14 @@ class TestFunctionNameInspection : NamingConventionInspection(
START_LOWER START_LOWER
) { ) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return namedFunctionVisitor { function ->
override fun visitNamedFunction(function: KtNamedFunction) { if (!TestUtils.isInTestSourceContent(function)) {
if (!TestUtils.isInTestSourceContent(function)) { return@namedFunctionVisitor
return
}
if (function.nameIdentifier?.text?.startsWith("`") == true) {
return
}
verifyName(function, holder)
} }
if (function.nameIdentifier?.text?.startsWith("`") == true) {
return@namedFunctionVisitor
}
verifyName(function, holder)
} }
} }
} }
@@ -178,11 +169,9 @@ abstract class PropertyNameInspectionBase protected constructor(
protected enum class PropertyKind { NORMAL, PRIVATE, OBJECT_OR_TOP_LEVEL, CONST, LOCAL } protected enum class PropertyKind { NORMAL, PRIVATE, OBJECT_OR_TOP_LEVEL, CONST, LOCAL }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return propertyVisitor { property ->
override fun visitProperty(property: KtProperty) { if (property.getKind() == kind) {
if (property.getKind() == kind) { verifyName(property, holder)
verifyName(property, holder)
}
} }
} }
} }
@@ -234,17 +223,15 @@ class LocalVariableNameInspection :
class PackageNameInspection : class PackageNameInspection :
NamingConventionInspection("Package", "[a-z][A-Za-z\\d]*(\\.[a-z][A-Za-z\\d]*)*", NO_UNDERSCORES) { NamingConventionInspection("Package", "[a-z][A-Za-z\\d]*(\\.[a-z][A-Za-z\\d]*)*", NO_UNDERSCORES) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return packageDirectiveVisitor { directive ->
override fun visitPackageDirective(directive: KtPackageDirective) { val qualifiedName = directive.qualifiedName
val qualifiedName = directive.qualifiedName if (qualifiedName.isNotEmpty() && nameRegex?.matches(qualifiedName) == false) {
if (qualifiedName.isNotEmpty() && nameRegex?.matches(qualifiedName) == false) { val message = getNameMismatchMessage(qualifiedName)
val message = getNameMismatchMessage(qualifiedName) holder.registerProblem(
holder.registerProblem( directive.packageNameExpression!!,
directive.packageNameExpression!!, "Package name <code>#ref</code> $message #loc",
"Package name <code>#ref</code> $message #loc", RenamePackageFix()
RenamePackageFix() )
)
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -31,14 +20,12 @@ import org.jetbrains.kotlin.types.TypeUtils
class NullChecksToSafeCallInspection : AbstractKotlinInspection() { class NullChecksToSafeCallInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { binaryExpressionVisitor { expression ->
override fun visitBinaryExpression(expression: KtBinaryExpression) { if (isNullChecksToSafeCallFixAvailable(expression)) {
if (isNullChecksToSafeCallFixAvailable(expression)) { holder.registerProblem(expression,
holder.registerProblem(expression, "Null-checks replaceable with safe-calls",
"Null-checks replaceable with safe-calls", ProblemHighlightType.WEAK_WARNING,
ProblemHighlightType.WEAK_WARNING, NullChecksToSafeCallCheckFix())
NullChecksToSafeCallCheckFix())
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -34,36 +23,34 @@ import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
class NullableBooleanElvisInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class NullableBooleanElvisInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { binaryExpressionVisitor(fun(expression) {
override fun visitBinaryExpression(expression: KtBinaryExpression) { if (expression.operationToken != KtTokens.ELVIS) return
if (expression.operationToken != KtTokens.ELVIS) return val lhs = expression.left ?: return
val lhs = expression.left ?: return val rhs = expression.right ?: return
val rhs = expression.right ?: return if (!KtPsiUtil.isBooleanConstant(rhs)) return
if (!KtPsiUtil.isBooleanConstant(rhs)) return val lhsType = lhs.analyze(BodyResolveMode.PARTIAL).getType(lhs) ?: return
val lhsType = lhs.analyze(BodyResolveMode.PARTIAL).getType(lhs) ?: return if (TypeUtils.isNullableType(lhsType) && lhsType.isBooleanOrNullableBoolean()) {
if (TypeUtils.isNullableType(lhsType) && lhsType.isBooleanOrNullableBoolean()) { val parentIfOrWhile = PsiTreeUtil.getParentOfType(
val parentIfOrWhile = PsiTreeUtil.getParentOfType( expression, KtIfExpression::class.java, KtWhileExpressionBase::class.java)
expression, KtIfExpression::class.java, KtWhileExpressionBase::class.java) val condition = when (parentIfOrWhile) {
val condition = when (parentIfOrWhile) { is KtIfExpression -> parentIfOrWhile.condition
is KtIfExpression -> parentIfOrWhile.condition is KtWhileExpressionBase -> parentIfOrWhile.condition
is KtWhileExpressionBase -> parentIfOrWhile.condition else -> null
else -> null
}
val (highlightType, verb) = if (condition != null && condition in expression.parentsWithSelf)
GENERIC_ERROR_OR_WARNING to "should"
else
INFORMATION to "can"
holder.registerProblemWithoutOfflineInformation(
expression,
"Equality check $verb be used instead of elvis for nullable boolean check",
isOnTheFly,
highlightType,
ReplaceWithEqualityCheckFix()
)
}
} }
val (highlightType, verb) = if (condition != null && condition in expression.parentsWithSelf)
GENERIC_ERROR_OR_WARNING to "should"
else
INFORMATION to "can"
holder.registerProblemWithoutOfflineInformation(
expression,
"Equality check $verb be used instead of elvis for nullable boolean check",
isOnTheFly,
highlightType,
ReplaceWithEqualityCheckFix()
)
} }
})
private class ReplaceWithEqualityCheckFix : LocalQuickFix { private class ReplaceWithEqualityCheckFix : LocalQuickFix {
override fun getName() = "Replace with equality check" override fun getName() = "Replace with equality check"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -24,33 +13,30 @@ import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.addRemoveModifier.addModifier import org.jetbrains.kotlin.psi.addRemoveModifier.addModifier
import org.jetbrains.kotlin.psi.declarationVisitor
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
class ProtectedInFinalInspection : AbstractKotlinInspection() { class ProtectedInFinalInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return declarationVisitor(fun(declaration) {
override fun visitDeclaration(declaration: KtDeclaration) { val visibilityModifier = declaration.visibilityModifier() ?: return
val visibilityModifier = declaration.visibilityModifier() ?: return val modifierType = visibilityModifier.node?.elementType
val modifierType = visibilityModifier.node?.elementType if (modifierType == KtTokens.PROTECTED_KEYWORD) {
if (modifierType == KtTokens.PROTECTED_KEYWORD) { val parentClass = declaration.getParentOfType<KtClass>(true) ?: return
val parentClass = declaration.getParentOfType<KtClass>(true) ?: return if (!parentClass.isInheritable() && !parentClass.isEnum() &&
if (!parentClass.isInheritable() && !parentClass.isEnum() && declaration.implicitVisibility() != KtTokens.PROTECTED_KEYWORD) {
declaration.implicitVisibility() != KtTokens.PROTECTED_KEYWORD) { holder.registerProblem(visibilityModifier,
holder.registerProblem(visibilityModifier, "'protected' visibility is effectively 'private' in a final class",
"'protected' visibility is effectively 'private' in a final class", ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, MakePrivateFix(),
MakePrivateFix(), MakeOpenFix()
MakeOpenFix() )
)
}
} }
} }
} })
} }
class MakePrivateFix : LocalQuickFix { class MakePrivateFix : LocalQuickFix {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -35,20 +24,17 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
class RecursivePropertyAccessorInspection : AbstractKotlinInspection() { class RecursivePropertyAccessorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return simpleNameExpressionVisitor { expression ->
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { if (isRecursivePropertyAccess(expression)) {
super.visitSimpleNameExpression(expression) holder.registerProblem(expression,
if (isRecursivePropertyAccess(expression)) { "Recursive property accessor",
holder.registerProblem(expression, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
"Recursive property accessor", ReplaceWithFieldFix())
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, }
ReplaceWithFieldFix()) else if (isRecursiveSyntheticPropertyAccess(expression)) {
} holder.registerProblem(expression,
else if (isRecursiveSyntheticPropertyAccess(expression)) { "Recursive synthetic property accessor",
holder.registerProblem(expression, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
"Recursive synthetic property accessor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -29,61 +18,57 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RedundantExplicitTypeInspection : AbstractKotlinInspection() { class RedundantExplicitTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() { propertyVisitor(fun(property) {
override fun visitProperty(property: KtProperty) { if (!property.isLocal) return
super.visitProperty(property) val typeReference = property.typeReference ?: return
val initializer = property.initializer ?: return
if (!property.isLocal) return val type = property.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return
val typeReference = property.typeReference ?: return when (initializer) {
val initializer = property.initializer ?: return is KtConstantExpression -> {
when (initializer.node.elementType) {
val type = property.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return KtNodeTypes.BOOLEAN_CONSTANT -> {
when (initializer) { if (!KotlinBuiltIns.isBoolean(type)) return
is KtConstantExpression -> {
when (initializer.node.elementType) {
KtNodeTypes.BOOLEAN_CONSTANT -> {
if (!KotlinBuiltIns.isBoolean(type)) return
}
KtNodeTypes.INTEGER_CONSTANT -> {
if (initializer.text.endsWith("L")) {
if (!KotlinBuiltIns.isLong(type)) return
}
else {
if (!KotlinBuiltIns.isInt(type)) return
}
}
KtNodeTypes.FLOAT_CONSTANT -> {
if (initializer.text.endsWith("f") || initializer.text.endsWith("F")) {
if (!KotlinBuiltIns.isFloat(type)) return
}
else {
if (!KotlinBuiltIns.isDouble(type)) return
}
}
KtNodeTypes.CHARACTER_CONSTANT -> {
if (!KotlinBuiltIns.isChar(type)) return
}
else -> return
} }
KtNodeTypes.INTEGER_CONSTANT -> {
if (initializer.text.endsWith("L")) {
if (!KotlinBuiltIns.isLong(type)) return
}
else {
if (!KotlinBuiltIns.isInt(type)) return
}
}
KtNodeTypes.FLOAT_CONSTANT -> {
if (initializer.text.endsWith("f") || initializer.text.endsWith("F")) {
if (!KotlinBuiltIns.isFloat(type)) return
}
else {
if (!KotlinBuiltIns.isDouble(type)) return
}
}
KtNodeTypes.CHARACTER_CONSTANT -> {
if (!KotlinBuiltIns.isChar(type)) return
}
else -> return
} }
is KtStringTemplateExpression -> {
if (!KotlinBuiltIns.isString(type)) return
}
is KtNameReferenceExpression -> {
if (typeReference.text != initializer.getReferencedName()) return
}
is KtCallExpression -> {
if (typeReference.text != initializer.calleeExpression?.text) return
}
else -> return
} }
is KtStringTemplateExpression -> {
holder.registerProblem( if (!KotlinBuiltIns.isString(type)) return
typeReference, }
"Explicitly given type is redundant here", is KtNameReferenceExpression -> {
ProblemHighlightType.LIKE_UNUSED_SYMBOL, if (typeReference.text != initializer.getReferencedName()) return
IntentionWrapper(RemoveExplicitTypeIntention(), property.containingKtFile) }
) is KtCallExpression -> {
if (typeReference.text != initializer.calleeExpression?.text) return
}
else -> return
} }
}
holder.registerProblem(
typeReference,
"Explicitly given type is redundant here",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), property.containingKtFile)
)
})
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -20,19 +9,15 @@ import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class RedundantGetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantGetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return propertyAccessorVisitor { accessor ->
override fun visitPropertyAccessor(accessor: KtPropertyAccessor) { if (accessor.isRedundantGetter()) {
super.visitPropertyAccessor(accessor) holder.registerProblem(accessor,
if (accessor.isRedundantGetter()) { "Redundant getter",
holder.registerProblem(accessor, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
"Redundant getter", RemoveRedundantGetterFix())
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantGetterFix())
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -27,18 +16,15 @@ import org.jetbrains.kotlin.psi.*
class RedundantIfInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantIfInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return ifExpressionVisitor { expression ->
override fun visitIfExpression(expression: KtIfExpression) { if (expression.condition == null) return@ifExpressionVisitor
super.visitIfExpression(expression) val (redundancyType, branchType) = RedundancyType.of(expression)
if (expression.condition == null) return if (redundancyType == RedundancyType.NONE) return@ifExpressionVisitor
val (redundancyType, branchType) = RedundancyType.of(expression)
if (redundancyType == RedundancyType.NONE) return
holder.registerProblem(expression, holder.registerProblem(expression,
"Redundant 'if' statement", "Redundant 'if' statement",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantIf(redundancyType, branchType)) RemoveRedundantIf(redundancyType, branchType))
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -23,23 +12,20 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.lambdaExpressionVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
class RedundantLambdaArrowInspection : AbstractKotlinInspection() { class RedundantLambdaArrowInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return lambdaExpressionVisitor { lambdaExpression ->
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) { val functionLiteral = lambdaExpression.functionLiteral
val functionLiteral = lambdaExpression.functionLiteral if (functionLiteral.valueParameters.isNotEmpty()) return@lambdaExpressionVisitor
if (functionLiteral.valueParameters.isNotEmpty()) return val arrow = functionLiteral.arrow ?: return@lambdaExpressionVisitor
val arrow = functionLiteral.arrow ?: return
holder.registerProblem( holder.registerProblem(
arrow, arrow,
"Redundant lambda arrow", "Redundant lambda arrow",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, ProblemHighlightType.LIKE_UNUSED_SYMBOL,
DeleteFix()) DeleteFix())
}
} }
} }
@@ -1,53 +1,31 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.* import com.intellij.codeInspection.*
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.implicitModality import org.jetbrains.kotlin.idea.core.implicitModality
import org.jetbrains.kotlin.idea.core.mapModality
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.declarationVisitor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return declarationVisitor { declaration ->
override fun visitDeclaration(declaration: KtDeclaration) { val modalityModifier = declaration.modalityModifier() ?: return@declarationVisitor
val modalityModifier = declaration.modalityModifier() ?: return val modalityModifierType = modalityModifier.node.elementType
val modalityModifierType = modalityModifier.node.elementType val implicitModality = declaration.implicitModality()
val implicitModality = declaration.implicitModality()
if (modalityModifierType != implicitModality) return if (modalityModifierType != implicitModality) return@declarationVisitor
holder.registerProblem(modalityModifier, holder.registerProblem(modalityModifier,
"Redundant modality modifier", "Redundant modality modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(declaration, implicitModality, isRedundant = true), IntentionWrapper(RemoveModifierFix(declaration, implicitModality, isRedundant = true),
declaration.containingFile)) declaration.containingFile))
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -53,63 +42,60 @@ import org.jetbrains.kotlin.utils.keysToMapExceptNulls
class RedundantSamConstructorInspection : AbstractKotlinInspection() { class RedundantSamConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return callExpressionVisitor(fun(expression) {
private fun createQuickFix(expression: KtCallExpression): LocalQuickFix { if (expression.valueArguments.isEmpty()) return
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructor" val samConstructorCalls = samConstructorCallsToBeConverted(expression)
override fun getFamilyName() = name if (samConstructorCalls.isEmpty()) return
override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val single = samConstructorCalls.singleOrNull()
if (!FileModificationService.getInstance().preparePsiElementForWrite(expression)) return if (single != null) {
replaceSamConstructorCall(expression) val calleeExpression = single.calleeExpression ?: return
} val problemDescriptor = holder.manager.
} createProblemDescriptor(calleeExpression,
single.typeArgumentList ?: calleeExpression,
"Redundant SAM-constructor",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
createQuickFix(single))
holder.registerProblem(problemDescriptor)
} }
else {
val problemDescriptor = holder.manager.
createProblemDescriptor(expression.valueArgumentList!!,
"Redundant SAM-constructors",
createQuickFix(samConstructorCalls),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly)
private fun createQuickFix(expressions: Collection<KtCallExpression>): LocalQuickFix { holder.registerProblem(problemDescriptor)
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructors"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
for (callExpression in expressions) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(callExpression)) return
replaceSamConstructorCall(callExpression)
}
}
}
} }
})
}
override fun visitCallExpression(expression: KtCallExpression) { private fun createQuickFix(expression: KtCallExpression): LocalQuickFix {
if (expression.valueArguments.isEmpty()) return return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructor"
val samConstructorCalls = samConstructorCallsToBeConverted(expression) override fun getFamilyName() = name
if (samConstructorCalls.isEmpty()) return override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val single = samConstructorCalls.singleOrNull() if (!FileModificationService.getInstance().preparePsiElementForWrite(expression)) return
if (single != null) { replaceSamConstructorCall(expression)
val calleeExpression = single.calleeExpression ?: return
val problemDescriptor = holder.manager.
createProblemDescriptor(calleeExpression,
single.typeArgumentList ?: calleeExpression,
"Redundant SAM-constructor",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
createQuickFix(single))
holder.registerProblem(problemDescriptor)
}
else {
val problemDescriptor = holder.manager.
createProblemDescriptor(expression.valueArgumentList!!,
"Redundant SAM-constructors",
createQuickFix(samConstructorCalls),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly)
holder.registerProblem(problemDescriptor)
}
} }
} }
} }
private fun createQuickFix(expressions: Collection<KtCallExpression>): LocalQuickFix {
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructors"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
for (callExpression in expressions) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(callExpression)) return
replaceSamConstructorCall(callExpression)
}
}
}
}
companion object { companion object {
fun replaceSamConstructorCall(callExpression: KtCallExpression): KtLambdaExpression { fun replaceSamConstructorCall(callExpression: KtCallExpression): KtLambdaExpression {
val functionalArgument = callExpression.samConstructorValueArgument()?.getArgumentExpression() val functionalArgument = callExpression.samConstructorValueArgument()?.getArgumentExpression()
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -25,15 +14,12 @@ import org.jetbrains.kotlin.psi.*
class RedundantSetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantSetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return propertyAccessorVisitor { accessor ->
override fun visitPropertyAccessor(accessor: KtPropertyAccessor) { if (accessor.isRedundantSetter()) {
super.visitPropertyAccessor(accessor) holder.registerProblem(accessor,
if (accessor.isRedundantSetter()) { "Redundant setter",
holder.registerProblem(accessor, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
"Redundant setter", RemoveRedundantSetterFix())
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantSetterFix())
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -24,43 +13,37 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.core.getModalityFromDescriptor
import org.jetbrains.kotlin.idea.highlighter.hasSuspendCalls import org.jetbrains.kotlin.idea.highlighter.hasSuspendCalls
import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.namedFunctionVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
class RedundantSuspendModifierInspection : AbstractKotlinInspection() { class RedundantSuspendModifierInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return namedFunctionVisitor(fun(function) {
override fun visitNamedFunction(function: KtNamedFunction) { if (!function.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) return
super.visitNamedFunction(function)
if (!function.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) return
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
if (!function.hasBody()) return if (!function.hasBody()) return
if (function.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return if (function.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
val context = function.analyzeFully() val context = function.analyzeFully()
val descriptor = context[BindingContext.FUNCTION, function] ?: return val descriptor = context[BindingContext.FUNCTION, function] ?: return
if (descriptor.modality == Modality.OPEN) return if (descriptor.modality == Modality.OPEN) return
if (function.anyDescendantOfType<KtExpression> { it.hasSuspendCalls(context) }) {
return
}
holder.registerProblem(suspendModifier,
"Redundant 'suspend' modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(function, KtTokens.SUSPEND_KEYWORD, isRedundant = true),
function.containingFile))
if (function.anyDescendantOfType<KtExpression> { it.hasSuspendCalls(context) }) {
return
} }
}
holder.registerProblem(suspendModifier,
"Redundant 'suspend' modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(function, KtTokens.SUSPEND_KEYWORD, isRedundant = true),
function.containingFile))
})
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -25,27 +14,22 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return referenceExpressionVisitor(fun(expression) {
if (KotlinBuiltIns.FQ_NAMES.unit.shortName() != (expression as? KtNameReferenceExpression)?.getReferencedNameAsName()) {
override fun visitReferenceExpression(expression: KtReferenceExpression) { return
super.visitReferenceExpression(expression)
if (KotlinBuiltIns.FQ_NAMES.unit.shortName() != (expression as? KtNameReferenceExpression)?.getReferencedNameAsName()) {
return
}
val parent = expression.parent
if (parent !is KtReturnExpression && parent !is KtBlockExpression) return
// Do not report just 'Unit' in function literals (return@label Unit is OK even in literals)
if (parent is KtBlockExpression && parent.getParentOfType<KtFunctionLiteral>(strict = true) != null) return
holder.registerProblem(expression,
"Redundant 'Unit'",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveRedundantUnitFix())
} }
}
val parent = expression.parent
if (parent !is KtReturnExpression && parent !is KtBlockExpression) return
// Do not report just 'Unit' in function literals (return@label Unit is OK even in literals)
if (parent is KtBlockExpression && parent.getParentOfType<KtFunctionLiteral>(strict = true) != null) return
holder.registerProblem(expression,
"Redundant 'Unit'",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveRedundantUnitFix())
})
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -24,34 +13,28 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.namedFunctionVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
class RedundantUnitReturnTypeInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantUnitReturnTypeInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return namedFunctionVisitor(fun(function) {
override fun visitNamedFunction(function: KtNamedFunction) { if (function.containingFile is KtCodeFragment) return
super.visitNamedFunction(function) val typeElement = function.typeReference?.typeElement ?: return
if (function.containingFile is KtCodeFragment) return val context = function.analyze(BodyResolveMode.PARTIAL)
val typeElement = function.typeReference?.typeElement ?: return val descriptor = context[BindingContext.FUNCTION, function] ?: return
val context = function.analyze(BodyResolveMode.PARTIAL) if (descriptor.returnType?.isUnit() == true) {
val descriptor = context[BindingContext.FUNCTION, function] ?: return if (!function.hasBlockBody()) {
if (descriptor.returnType?.isUnit() == true) { return
if (!function.hasBlockBody()) {
return
}
holder.registerProblem(typeElement,
"Redundant 'Unit' return type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), function.containingKtFile))
} }
holder.registerProblem(typeElement,
"Redundant 'Unit' return type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), function.containingKtFile))
} }
} })
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -20,23 +9,20 @@ import com.intellij.codeInspection.*
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.core.implicitVisibility import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.declarationVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
class RedundantVisibilityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RedundantVisibilityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return declarationVisitor { declaration ->
override fun visitDeclaration(declaration: KtDeclaration) { val visibilityModifier = declaration.visibilityModifier() ?: return@declarationVisitor
val visibilityModifier = declaration.visibilityModifier() ?: return val implicitVisibility = declaration.implicitVisibility()
val implicitVisibility = declaration.implicitVisibility() if (visibilityModifier.node.elementType == implicitVisibility) {
if (visibilityModifier.node.elementType == implicitVisibility) { holder.registerProblem(visibilityModifier,
holder.registerProblem(visibilityModifier, "Redundant visibility modifier",
"Redundant visibility modifier", ProblemHighlightType.LIKE_UNUSED_SYMBOL,
ProblemHighlightType.LIKE_UNUSED_SYMBOL, IntentionWrapper(RemoveModifierFix(declaration, implicitVisibility, isRedundant = true),
IntentionWrapper(RemoveModifierFix(declaration, implicitVisibility, isRedundant = true), declaration.containingFile))
declaration.containingFile))
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -31,36 +20,32 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
class RemoveRedundantSpreadOperatorInspection : AbstractKotlinInspection() { class RemoveRedundantSpreadOperatorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return valueArgumentVisitor(fun(argument) {
override fun visitArgument(argument: KtValueArgument) { val spreadElement = argument.getSpreadElement() ?: return
super.visitArgument(argument) if (argument.isNamed()) return
val argumentExpression = argument.getArgumentExpression() ?: return
val spreadElement = argument.getSpreadElement() ?: return val argumentOffset = argument.startOffset
if (argument.isNamed()) return val startOffset = spreadElement.startOffset - argumentOffset
val argumentExpression = argument.getArgumentExpression() ?: return val endOffset =
val argumentOffset = argument.startOffset when (argumentExpression) {
val startOffset = spreadElement.startOffset - argumentOffset is KtCallExpression -> {
val endOffset = if (!argumentExpression.isArrayOfMethod()) return
when (argumentExpression) { argumentExpression.calleeExpression!!.endOffset - argumentOffset
is KtCallExpression -> {
if (!argumentExpression.isArrayOfMethod()) return
argumentExpression.calleeExpression!!.endOffset - argumentOffset
}
is KtCollectionLiteralExpression -> startOffset + 1
else -> return
} }
is KtCollectionLiteralExpression -> startOffset + 1
else -> return
}
val problemDescriptor = holder.manager.createProblemDescriptor( val problemDescriptor = holder.manager.createProblemDescriptor(
argument, argument,
TextRange(startOffset, endOffset), TextRange(startOffset, endOffset),
"Remove redundant spread operator", "Remove redundant spread operator",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly, isOnTheFly,
RemoveRedundantSpreadOperatorQuickfix() RemoveRedundantSpreadOperatorQuickfix()
) )
holder.registerProblem(problemDescriptor) holder.registerProblem(problemDescriptor)
} })
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -22,20 +11,16 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention
import org.jetbrains.kotlin.idea.intentions.isSetterParameter import org.jetbrains.kotlin.idea.intentions.isSetterParameter
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.parameterVisitor
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtVisitorVoid
class RemoveSetterParameterTypeInspection : AbstractKotlinInspection() { class RemoveSetterParameterTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return parameterVisitor { dcl ->
override fun visitDeclaration(dcl: KtDeclaration) { val typeReference = dcl.takeIf { it.isSetterParameter }?.typeReference ?: return@parameterVisitor
val typeReference = (dcl as? KtParameter)?.takeIf { it.isSetterParameter }?.typeReference ?: return holder.registerProblem(typeReference,
holder.registerProblem(typeReference, "Redundant setter parameter type",
"Redundant setter parameter type", ProblemHighlightType.LIKE_UNUSED_SYMBOL,
ProblemHighlightType.LIKE_UNUSED_SYMBOL, IntentionWrapper(RemoveExplicitTypeIntention(), dcl.containingKtFile))
IntentionWrapper(RemoveExplicitTypeIntention(), dcl.containingKtFile))
}
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -29,27 +18,23 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RemoveToStringInStringTemplateInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class RemoveToStringInStringTemplateInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { dotQualifiedExpressionVisitor(fun(expression) {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { if (expression.parent !is KtBlockStringTemplateEntry) return
super.visitDotQualifiedExpression(expression) if (expression.receiverExpression is KtSuperExpression) return
val selectorExpression = expression.selectorExpression ?: return
if (!expression.isToString()) return
if (expression.parent !is KtBlockStringTemplateEntry) return holder.registerProblem(selectorExpression,
if (expression.receiverExpression is KtSuperExpression) return "Redundant 'toString()' call in string template",
val selectorExpression = expression.selectorExpression ?: return ProblemHighlightType.LIKE_UNUSED_SYMBOL,
if (!expression.isToString()) return RemoveToStringFix())
})
}
holder.registerProblem(selectorExpression, private fun KtDotQualifiedExpression.isToString(): Boolean {
"Redundant 'toString()' call in string template", val resolvedCall = toResolvedCall(BodyResolveMode.PARTIAL) ?: return false
ProblemHighlightType.LIKE_UNUSED_SYMBOL, val callableDescriptor = resolvedCall.resultingDescriptor as? CallableMemberDescriptor ?: return false
RemoveToStringFix()) return callableDescriptor.getDeepestSuperDeclarations().any { it.fqNameUnsafe.asString() == "kotlin.Any.toString" }
}
private fun KtDotQualifiedExpression.isToString(): Boolean {
val resolvedCall = toResolvedCall(BodyResolveMode.PARTIAL) ?: return false
val callableDescriptor = resolvedCall.resultingDescriptor as? CallableMemberDescriptor ?: return false
return callableDescriptor.getDeepestSuperDeclarations().any { it.fqNameUnsafe.asString() == "kotlin.Any.toString" }
}
}
} }
class RemoveToStringFix: LocalQuickFix { class RemoveToStringFix: LocalQuickFix {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -32,39 +21,35 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class ReplaceArrayOfWithLiteralInspection : AbstractKotlinInspection() { class ReplaceArrayOfWithLiteralInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return callExpressionVisitor(fun(expression) {
override fun visitCallExpression(expression: KtCallExpression) { if (!expression.languageVersionSettings.supportsFeature(ArrayLiteralsInAnnotations) &&
super.visitCallExpression(expression) !ApplicationManager.getApplication().isUnitTestMode) return
if (!expression.languageVersionSettings.supportsFeature(ArrayLiteralsInAnnotations) && val calleeExpression = expression.calleeExpression as? KtNameReferenceExpression ?: return
!ApplicationManager.getApplication().isUnitTestMode) return if (!expression.isArrayOfMethod()) return
val calleeExpression = expression.calleeExpression as? KtNameReferenceExpression ?: return val parent = expression.parent
if (!expression.isArrayOfMethod()) return when (parent) {
is KtValueArgument -> {
val parent = expression.parent if (parent.parent.parent !is KtAnnotationEntry) return
when (parent) { if (parent.getSpreadElement() != null && !parent.isNamed()) return
is KtValueArgument -> {
if (parent.parent.parent !is KtAnnotationEntry) return
if (parent.getSpreadElement() != null && !parent.isNamed()) return
}
is KtParameter -> {
val constructor = parent.parent.parent as? KtPrimaryConstructor ?: return
val containingClass = constructor.getContainingClassOrObject()
if (!containingClass.isAnnotation()) return
}
else -> return
} }
is KtParameter -> {
val calleeName = calleeExpression.getReferencedName() val constructor = parent.parent.parent as? KtPrimaryConstructor ?: return
holder.registerProblem( val containingClass = constructor.getContainingClassOrObject()
calleeExpression, if (!containingClass.isAnnotation()) return
"'$calleeName' call should be replaced with array literal [...]", }
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, else -> return
ReplaceWithArrayLiteralFix()
)
} }
}
val calleeName = calleeExpression.getReferencedName()
holder.registerProblem(
calleeExpression,
"'$calleeName' call should be replaced with array literal [...]",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithArrayLiteralFix()
)
})
} }
private class ReplaceWithArrayLiteralFix : LocalQuickFix { private class ReplaceWithArrayLiteralFix : LocalQuickFix {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -26,7 +15,10 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.intentions.calleeName import org.jetbrains.kotlin.idea.intentions.calleeName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.psi.dotQualifiedExpressionVisitor
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -34,26 +26,23 @@ class ReplaceToWithInfixFormInspection : AbstractKotlinInspection() {
private val compatibleNames = setOf("to") private val compatibleNames = setOf("to")
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return dotQualifiedExpressionVisitor(fun(expression) {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { if (expression.callExpression?.valueArguments?.size != 1) return
super.visitDotQualifiedExpression(expression) if (expression.calleeName !in compatibleNames) return
if (expression.callExpression?.valueArguments?.size != 1) return
if (expression.calleeName !in compatibleNames) return
val context = expression.analyze(BodyResolveMode.PARTIAL) val context = expression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = expression.getResolvedCall(context) ?: return val resolvedCall = expression.getResolvedCall(context) ?: return
val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!function.isInfix) return if (!function.isInfix) return
holder.registerProblem( holder.registerProblem(
expression, expression,
"Replace 'to' with infix form", "Replace 'to' with infix form",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceToWithInfixFormQuickfix() ReplaceToWithInfixFormQuickfix()
) )
} })
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -31,64 +20,59 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
class SelfAssignmentInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { class SelfAssignmentInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return binaryExpressionVisitor(fun(expression) {
if (expression.operationToken != KtTokens.EQ) return
val left = expression.left
val leftRefExpr = left?.asNameReferenceExpression() ?: return
val right = expression.right
val rightRefExpr = right?.asNameReferenceExpression() ?: return
// To omit analyzing too much
if (leftRefExpr.text != rightRefExpr.text) return
private fun KtExpression.asNameReferenceExpression(): KtNameReferenceExpression? = when (this) { val context = expression.analyze(BodyResolveMode.PARTIAL)
is KtNameReferenceExpression -> val leftResolvedCall = left.getResolvedCall(context)
this val leftCallee = leftResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
is KtDotQualifiedExpression -> val rightResolvedCall = right.getResolvedCall(context)
(selectorExpression as? KtNameReferenceExpression)?.takeIf { receiverExpression is KtThisExpression } val rightCallee = rightResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
else -> if (leftCallee != rightCallee) return
null
if (!rightCallee.isVar) return
if (rightCallee is PropertyDescriptor) {
if (rightCallee.isOverridable) return
if (rightCallee.accessors.any { !it.isDefault }) return
} }
private fun KtExpression.receiverDeclarationDescriptor( if (left.receiverDeclarationDescriptor(leftResolvedCall, context) !=
resolvedCall: ResolvedCall<out CallableDescriptor>, right.receiverDeclarationDescriptor(rightResolvedCall, context)) {
context: BindingContext return
): DeclarationDescriptor? {
val thisExpression = (this as? KtDotQualifiedExpression)?.receiverExpression as? KtThisExpression
if (thisExpression != null) {
return thisExpression.getResolvedCall(context)?.resultingDescriptor?.containingDeclaration
}
val implicitReceiver = with (resolvedCall) { dispatchReceiver ?: extensionReceiver } as? ImplicitReceiver
return implicitReceiver?.declarationDescriptor
} }
override fun visitBinaryExpression(expression: KtBinaryExpression) { holder.registerProblem(right,
super.visitBinaryExpression(expression) "Variable '${rightCallee.name}' is assigned to itself",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveSelfAssignmentFix())
})
}
if (expression.operationToken != KtTokens.EQ) return private fun KtExpression.asNameReferenceExpression(): KtNameReferenceExpression? = when (this) {
val left = expression.left is KtNameReferenceExpression ->
val leftRefExpr = left?.asNameReferenceExpression() ?: return this
val right = expression.right is KtDotQualifiedExpression ->
val rightRefExpr = right?.asNameReferenceExpression() ?: return (selectorExpression as? KtNameReferenceExpression)?.takeIf { receiverExpression is KtThisExpression }
// To omit analyzing too much else ->
if (leftRefExpr.text != rightRefExpr.text) return null
}
val context = expression.analyze(BodyResolveMode.PARTIAL) private fun KtExpression.receiverDeclarationDescriptor(
val leftResolvedCall = left.getResolvedCall(context) resolvedCall: ResolvedCall<out CallableDescriptor>,
val leftCallee = leftResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return context: BindingContext
val rightResolvedCall = right.getResolvedCall(context) ): DeclarationDescriptor? {
val rightCallee = rightResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return val thisExpression = (this as? KtDotQualifiedExpression)?.receiverExpression as? KtThisExpression
if (leftCallee != rightCallee) return if (thisExpression != null) {
return thisExpression.getResolvedCall(context)?.resultingDescriptor?.containingDeclaration
if (!rightCallee.isVar) return
if (rightCallee is PropertyDescriptor) {
if (rightCallee.isOverridable) return
if (rightCallee.accessors.any { !it.isDefault }) return
}
if (left.receiverDeclarationDescriptor(leftResolvedCall, context) !=
right.receiverDeclarationDescriptor(rightResolvedCall, context)) {
return
}
holder.registerProblem(right,
"Variable '${rightCallee.name}' is assigned to itself",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveSelfAssignmentFix())
}
} }
val implicitReceiver = with (resolvedCall) { dispatchReceiver ?: extensionReceiver } as? ImplicitReceiver
return implicitReceiver?.declarationDescriptor
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -30,22 +19,16 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class SimplifyWhenWithBooleanConstantConditionInspection : AbstractKotlinInspection() { class SimplifyWhenWithBooleanConstantConditionInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return whenExpressionVisitor(fun(expression) {
if (expression.closeBrace == null) return
if (expression.subjectExpression != null) return
if (expression.entries.none { it.isTrueConstantCondition() || it.isFalseConstantCondition() }) return
override fun visitWhenExpression(expression: KtWhenExpression) { holder.registerProblem(expression.whenKeyword,
super.visitWhenExpression(expression) "This 'when' is simplifiable",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
if (expression.closeBrace == null) return SimplifyWhenFix())
if (expression.subjectExpression != null) return })
if (expression.entries.none { it.isTrueConstantCondition() || it.isFalseConstantCondition() }) return
holder.registerProblem(expression.whenKeyword,
"This 'when' is simplifiable",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
SimplifyWhenFix())
}
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -24,18 +13,16 @@ import org.jetbrains.kotlin.psi.*
class SuspiciousEqualsCombination : AbstractKotlinInspection() { class SuspiciousEqualsCombination : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() { binaryExpressionVisitor(fun(expression) {
override fun visitBinaryExpression(expression: KtBinaryExpression) { if (expression.parent is KtBinaryExpression) return
if (expression.parent is KtBinaryExpression) return val operands = expression.parseBinary()
val operands = expression.parseBinary() val eqeq = operands.eqEqOperands.map { it.text }
val eqeq = operands.eqEqOperands.map { it.text } val eqeqeq = operands.eqEqEqOperands.map { it.text }
val eqeqeq = operands.eqEqEqOperands.map { it.text } if (eqeq.intersect(eqeqeq).isNotEmpty()) {
if (eqeq.intersect(eqeqeq).isNotEmpty()) { holder.registerProblem(expression, "Suspicious combination of == and ===",
holder.registerProblem(expression, "Suspicious combination of == and ===", ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
} }
} })
private fun KtBinaryExpression.parseBinary(pair: ComparisonOperands = ComparisonOperands()): ComparisonOperands { private fun KtBinaryExpression.parseBinary(pair: ComparisonOperands = ComparisonOperands()): ComparisonOperands {
when (operationToken) { when (operationToken) {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -20,8 +9,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.expressionVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -30,18 +18,14 @@ import org.jetbrains.kotlin.types.isDynamic
class UnsafeCastFromDynamicInspection : AbstractKotlinInspection() { class UnsafeCastFromDynamicInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return expressionVisitor(fun(expression) {
override fun visitExpression(expression: KtExpression) { val context = expression.analyze(BodyResolveMode.PARTIAL)
super.visitExpression(expression) val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, expression] ?: return
val actualType = expression.getType(context) ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL) if (actualType.isDynamic() && !expectedType.isDynamic() && !TypeUtils.noExpectedType(expectedType)) {
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, expression] ?: return holder.registerProblem(expression, "Implicit (unsafe) cast from dynamic to $expectedType")
val actualType = expression.getType(context) ?: return
if (actualType.isDynamic() && !expectedType.isDynamic() && !TypeUtils.noExpectedType(expectedType)) {
holder.registerProblem(expression, "Implicit (unsafe) cast from dynamic to $expectedType")
}
} }
} })
} }
} }
@@ -1,25 +1,12 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.FileModificationService import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.LocalQuickFixOnPsiElement import com.intellij.codeInspection.LocalQuickFixOnPsiElement
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -28,7 +15,10 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.callExpressionVisitor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -37,28 +27,26 @@ import org.jetbrains.kotlin.resolve.source.getPsi
class UnusedLambdaExpressionBodyInspection : AbstractKotlinInspection() { class UnusedLambdaExpressionBodyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return callExpressionVisitor(fun(expression) {
override fun visitCallExpression(expression: KtCallExpression) { val context = expression.analyze(BodyResolveMode.PARTIAL)
val context = expression.analyze(BodyResolveMode.PARTIAL) if (expression.used(context)) {
if (expression.used(context)) { return
return
}
val descriptor = expression.getResolvedCall(context)?.resultingDescriptor ?: return
if (!descriptor.returnsFunction()) {
return
}
val function = descriptor.source.getPsi() as? KtFunction ?: return
if (function.hasBlockBody() || function.bodyExpression !is KtLambdaExpression) {
return
}
holder.registerProblem(expression,
"Unused return value of a function with lambda expression body",
RemoveEqTokenFromFunctionDeclarationFix(function))
} }
}
val descriptor = expression.getResolvedCall(context)?.resultingDescriptor ?: return
if (!descriptor.returnsFunction()) {
return
}
val function = descriptor.source.getPsi() as? KtFunction ?: return
if (function.hasBlockBody() || function.bodyExpression !is KtLambdaExpression) {
return
}
holder.registerProblem(expression,
"Unused return value of a function with lambda expression body",
RemoveEqTokenFromFunctionDeclarationFix(function))
})
} }
private fun KtExpression.used(context: BindingContext): Boolean = context[BindingContext.USED_AS_EXPRESSION, this] ?: true private fun KtExpression.used(context: BindingContext): Boolean = context[BindingContext.USED_AS_EXPRESSION, this] ?: true
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -139,47 +128,44 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
override fun runForWholeFile() = true override fun runForWholeFile() = true
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return namedDeclarationVisitor(fun(declaration) {
override fun visitDeclaration(declaration: KtDeclaration) { val message = declaration.describe()?.let { "$it is never used" } ?: return
if (declaration !is KtNamedDeclaration) return
val message = declaration.describe()?.let { "$it is never used" } ?: return
if (!ProjectRootsUtil.isInProjectSource(declaration)) return if (!ProjectRootsUtil.isInProjectSource(declaration)) return
// Simple PSI-based checks // Simple PSI-based checks
if (declaration is KtObjectDeclaration && declaration.isCompanion()) return // never mark companion object as unused (there are too many reasons it can be needed for) if (declaration is KtObjectDeclaration && declaration.isCompanion()) return // never mark companion object as unused (there are too many reasons it can be needed for)
if (declaration is KtEnumEntry) return if (declaration is KtEnumEntry) return
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (declaration is KtProperty && declaration.isLocal) return if (declaration is KtProperty && declaration.isLocal) return
if (declaration is KtParameter && (declaration.getParent().parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return if (declaration is KtParameter && (declaration.getParent().parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return
// More expensive, resolve-based checks // More expensive, resolve-based checks
val descriptor = declaration.resolveToDescriptorIfAny() ?: return val descriptor = declaration.resolveToDescriptorIfAny() ?: return
if (descriptor is FunctionDescriptor && descriptor.isOperator) return if (descriptor is FunctionDescriptor && descriptor.isOperator) return
if (isEntryPoint(declaration)) return if (isEntryPoint(declaration)) return
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return
if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return
// properties can be referred by component1/component2, which is too expensive to search, don't mark them as unused // properties can be referred by component1/component2, which is too expensive to search, don't mark them as unused
if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return
// Main checks: finding reference usages && text usages // Main checks: finding reference usages && text usages
if (hasNonTrivialUsages(declaration, descriptor)) return if (hasNonTrivialUsages(declaration, descriptor)) return
if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return
val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return
val problemDescriptor = holder.manager.createProblemDescriptor( val problemDescriptor = holder.manager.createProblemDescriptor(
psiElement, psiElement,
null, null,
message, message,
ProblemHighlightType.LIKE_UNUSED_SYMBOL, ProblemHighlightType.LIKE_UNUSED_SYMBOL,
true, true,
*createQuickFixes(declaration).toTypedArray() *createQuickFixes(declaration).toTypedArray()
) )
holder.registerProblem(problemDescriptor) holder.registerProblem(problemDescriptor)
} })
}
} }
override val suppressionKey: String get() = "unused" override val suppressionKey: String get() = "unused"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -67,35 +56,31 @@ class UseExpressionBodyInspection(private val convertEmptyToUnit: Boolean) : Abs
} }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() { declarationVisitor(fun(declaration) {
override fun visitDeclaration(declaration: KtDeclaration) { declaration as? KtDeclarationWithBody ?: return
super.visitDeclaration(declaration) val (toHighlightElement, suffix, highlightType) = statusFor(declaration) ?: return
// Change range to start with left brace
declaration as? KtDeclarationWithBody ?: return val hasHighlighting = highlightType != INFORMATION
val (toHighlightElement, suffix, highlightType) = statusFor(declaration) ?: return val toHighlightRange = toHighlightElement?.textRange?.let {
// Change range to start with left brace if (hasHighlighting) {
val hasHighlighting = highlightType != INFORMATION it
val toHighlightRange = toHighlightElement?.textRange?.let { }
if (hasHighlighting) { else {
it // Extend range to [left brace..end of highlight element]
} val offset = (declaration.blockExpression()?.lBrace?.startOffset ?: it.startOffset) - it.startOffset
else { it.shiftRight(offset).grown(-offset)
// Extend range to [left brace..end of highlight element]
val offset = (declaration.blockExpression()?.lBrace?.startOffset ?: it.startOffset) - it.startOffset
it.shiftRight(offset).grown(-offset)
}
} }
holder.registerProblemWithoutOfflineInformation(
declaration,
"Use expression body instead of $suffix",
isOnTheFly,
highlightType,
toHighlightRange?.shiftRight(-declaration.startOffset),
ConvertToExpressionBodyFix()
)
} }
}
holder.registerProblemWithoutOfflineInformation(
declaration,
"Use expression body instead of $suffix",
isOnTheFly,
highlightType,
toHighlightRange?.shiftRight(-declaration.startOffset),
ConvertToExpressionBodyFix()
)
})
private fun KtDeclarationWithBody.findValueStatement(): KtExpression? { private fun KtDeclarationWithBody.findValueStatement(): KtExpression? {
val body = blockExpression() ?: return null val body = blockExpression() ?: return null
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -23,24 +12,22 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.whenExpressionVisitor
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class WhenWithOnlyElseInspection : AbstractKotlinInspection() { class WhenWithOnlyElseInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return whenExpressionVisitor { expression ->
override fun visitWhenExpression(expression: KtWhenExpression) { val singleEntry = expression.entries.singleOrNull()
val singleEntry = expression.entries.singleOrNull() if (singleEntry?.isElse != true) return@whenExpressionVisitor
if (singleEntry?.isElse != true) return
val usedAsExpression = expression.isUsedAsExpression(expression.analyze()) val usedAsExpression = expression.isUsedAsExpression(expression.analyze())
holder.registerProblem(expression, holder.registerProblem(expression,
"'when' has only 'else' branch and should be simplified", "'when' has only 'else' branch and should be simplified",
SimplifyFix(usedAsExpression) SimplifyFix(usedAsExpression)
) )
}
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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 package org.jetbrains.kotlin.idea.inspections
@@ -34,28 +23,25 @@ class WrapUnaryOperatorInspection : AbstractKotlinInspection() {
val numberTypes = listOf(KtNodeTypes.INTEGER_CONSTANT, KtNodeTypes.FLOAT_CONSTANT) val numberTypes = listOf(KtNodeTypes.INTEGER_CONSTANT, KtNodeTypes.FLOAT_CONSTANT)
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() { return prefixExpressionVisitor { expression ->
override fun visitPrefixExpression(expression: KtPrefixExpression) { if (expression.operationToken.isUnaryMinusOrPlus()) {
super.visitPrefixExpression(expression) val baseExpression = expression.baseExpression
if (expression.operationToken.isUnaryMinusOrPlus()) { if (baseExpression is KtDotQualifiedExpression) {
val baseExpression = expression.baseExpression val receiverExpression = baseExpression.receiverExpression
if (baseExpression is KtDotQualifiedExpression) { if (receiverExpression is KtConstantExpression &&
val receiverExpression = baseExpression.receiverExpression receiverExpression.node.elementType in numberTypes) {
if (receiverExpression is KtConstantExpression && holder.registerProblem(expression,
receiverExpression.node.elementType in numberTypes) { "Wrap unary operator and value with ()",
holder.registerProblem(expression, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
"Wrap unary operator and value with ()", WrapUnaryOperatorQuickfix())
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
WrapUnaryOperatorQuickfix())
}
} }
} }
} }
private fun IElementType.isUnaryMinusOrPlus() = this == KtTokens.MINUS || this == KtTokens.PLUS
} }
} }
private fun IElementType.isUnaryMinusOrPlus() = this == KtTokens.MINUS || this == KtTokens.PLUS
private class WrapUnaryOperatorQuickfix : LocalQuickFix { private class WrapUnaryOperatorQuickfix : LocalQuickFix {
override fun getName() = "Wrap unary operator and value with ()" override fun getName() = "Wrap unary operator and value with ()"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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.collections package org.jetbrains.kotlin.idea.inspections.collections
@@ -34,67 +23,63 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() { qualifiedExpressionVisitor(fun(expression) {
override fun visitQualifiedExpression(expression: KtQualifiedExpression) { val firstExpression = expression.receiverExpression
super.visitQualifiedExpression(expression) val firstCallExpression = getCallExpression(firstExpression) ?: return
val firstExpression = expression.receiverExpression val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return
val firstCallExpression = getCallExpression(firstExpression) ?: return
val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return val firstCalleeExpression = firstCallExpression.calleeExpression ?: return
val secondCalleeExpression = secondCallExpression.calleeExpression ?: return
val actualConversions = conversionGroups[
firstCalleeExpression.text to secondCalleeExpression.text
] ?: return
val firstCalleeExpression = firstCallExpression.calleeExpression ?: return val context = expression.analyze()
val secondCalleeExpression = secondCallExpression.calleeExpression ?: return val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return
val actualConversions = conversionGroups[ val conversion = actualConversions.firstOrNull {
firstCalleeExpression.text to secondCalleeExpression.text firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
] ?: return } ?: return
val context = expression.analyze() val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return
val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return
val conversion = actualConversions.firstOrNull {
firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
} ?: return
val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return // Do not apply on maps due to lack of relevant stdlib functions
val firstReceiverType = firstResolvedCall.extensionReceiver?.type
// Do not apply on maps due to lack of relevant stdlib functions val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
val firstReceiverType = firstResolvedCall.extensionReceiver?.type if (firstReceiverRawType != null) {
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
if (firstReceiverRawType != null) { firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)) return
if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)) return
}
// Do not apply for lambdas with return inside
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
if (lambdaArgument?.anyDescendantOfType<KtReturnExpression>() == true) return
val secondResolvedCall = expression.getResolvedCall(context) ?: return
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
parameter.type.isFunctionOfAnyKind() &&
resolvedArgument !is DefaultValueArgument
}) return
if (conversion.replacement.startsWith("joinTo")) {
// Function parameter in map must have String result type
if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) {
it.isSubtypeOf(builtIns.charSequence.defaultType)
}) return
}
val descriptor = holder.manager.createProblemDescriptor(
expression,
firstCalleeExpression.textRange.shiftRight(-expression.startOffset),
"Call chain on collection type may be simplified",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
SimplifyCallChainFix(conversion.replacement)
)
holder.registerProblem(descriptor)
} }
}
// Do not apply for lambdas with return inside
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
if (lambdaArgument?.anyDescendantOfType<KtReturnExpression>() == true) return
val secondResolvedCall = expression.getResolvedCall(context) ?: return
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
parameter.type.isFunctionOfAnyKind() &&
resolvedArgument !is DefaultValueArgument
}) return
if (conversion.replacement.startsWith("joinTo")) {
// Function parameter in map must have String result type
if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) {
it.isSubtypeOf(builtIns.charSequence.defaultType)
}) return
}
val descriptor = holder.manager.createProblemDescriptor(
expression,
firstCalleeExpression.textRange.shiftRight(-expression.startOffset),
"Call chain on collection type may be simplified",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
SimplifyCallChainFix(conversion.replacement)
)
holder.registerProblem(descriptor)
})
companion object { companion object {
@@ -11,7 +11,6 @@ import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.editor.EditorModificationUtil import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.siyeh.ig.psiutils.TestUtils import com.siyeh.ig.psiutils.TestUtils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
@@ -26,6 +25,7 @@ import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory
import org.jetbrains.kotlin.idea.kdoc.findKDoc import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.namedDeclarationVisitor
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -34,28 +34,22 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KDocMissingDocumentationInspection : AbstractKotlinInspection() { class KDocMissingDocumentationInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
KDocMissingDocumentationInspection(holder) namedDeclarationVisitor { element ->
if (TestUtils.isInTestSourceContent(element)) {
private class KDocMissingDocumentationInspection(private val holder: ProblemsHolder) : PsiElementVisitor() { return@namedDeclarationVisitor
override fun visitElement(element: PsiElement) { }
if (TestUtils.isInTestSourceContent(element)) {
return
}
if (element is KtNamedDeclaration) {
val nameIdentifier = element.nameIdentifier val nameIdentifier = element.nameIdentifier
val descriptor = element.resolveToDescriptorIfAny(BodyResolveMode.FULL) val descriptor = element.resolveToDescriptorIfAny(BodyResolveMode.FULL)
as? DeclarationDescriptorWithVisibility as? DeclarationDescriptorWithVisibility
as? MemberDescriptor ?: return as? MemberDescriptor ?: return@namedDeclarationVisitor
if (nameIdentifier != null && descriptor.isEffectivelyPublicApi) { if (nameIdentifier != null && descriptor.isEffectivelyPublicApi) {
if (descriptor.findKDoc { DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, it) } == null) { if (descriptor.findKDoc { DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, it) } == null) {
val message = element.describe()?.let { "$it is missing documentation" } ?: "Missing documentation" val message = element.describe()?.let { "$it is missing documentation" } ?: "Missing documentation"
holder.registerProblem(nameIdentifier, message, AddDocumentationFix()) holder.registerProblem(nameIdentifier, message, AddDocumentationFix())
} }
} }
} }
}
}
class AddDocumentationFix : LocalQuickFix { class AddDocumentationFix : LocalQuickFix {
override fun getName(): String = "Add documentation" override fun getName(): String = "Add documentation"