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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -23,7 +12,7 @@ import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
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.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -34,17 +23,13 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
abstract class AbstractPrimitiveRangeToInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitExpression(expression: KtExpression) {
super.visitExpression(expression)
return expressionVisitor { expression ->
if (expression !is KtBinaryExpression && expression !is KtDotQualifiedExpression) return@expressionVisitor
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
if (!fqName.matches(REGEX_RANGE_TO)) return
visitRangeToExpression(expression, holder)
}
visitRangeToExpression(expression, holder)
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -60,43 +49,41 @@ class AddVarianceModifierInspection : AbstractKotlinInspection() {
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
private fun variancePossible(
klass: KtClassOrObject,
parameterDescriptor: TypeParameterDescriptor,
variance: Variance,
context: BindingContext
) = VarianceCheckerCore(
context,
DiagnosticSink.DO_NOTHING,
ManualVariance(parameterDescriptor, variance)
).checkClassOrObject(klass)
override fun visitClassOrObject(klass: KtClassOrObject) {
val context = klass.analyzeFully()
for (typeParameter in klass.typeParameters) {
if (typeParameter.variance != Variance.INVARIANT) continue
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()
)
}
return classOrObjectVisitor { klass ->
val context = klass.analyzeFully()
for (typeParameter in klass.typeParameters) {
if (typeParameter.variance != Variance.INVARIANT) continue
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 {
override fun getName() = "Add '$variance' variance"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -26,7 +15,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -34,47 +23,45 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
class ArrayInDataClassInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitClass(klass: KtClass) {
if (!klass.isData()) return
val constructor = klass.primaryConstructor ?: return
if (hasOverriddenEqualsAndHashCode(klass)) return
val context = constructor.analyze(BodyResolveMode.PARTIAL)
for (parameter in constructor.valueParameters) {
if (!parameter.hasValOrVar()) continue
val type = context.get(BindingContext.TYPE, parameter.typeReference) ?: continue
if (KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) {
holder.registerProblem(parameter,
"Array property in data class: it's recommended to override equals() / hashCode()",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
GenerateEqualsAndHashcodeFix())
}
return classVisitor { klass ->
if (!klass.isData()) return@classVisitor
val constructor = klass.primaryConstructor ?: return@classVisitor
if (hasOverriddenEqualsAndHashCode(klass)) return@classVisitor
val context = constructor.analyze(BodyResolveMode.PARTIAL)
for (parameter in constructor.valueParameters) {
if (!parameter.hasValOrVar()) continue
val type = context.get(BindingContext.TYPE, parameter.typeReference) ?: continue
if (KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) {
holder.registerProblem(parameter,
"Array property in data class: it's recommended to override equals() / hashCode()",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
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 {
override fun getName() = "Generate equals() and hashCode()"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -78,46 +67,43 @@ class CanBeParameterInspection : AbstractKotlinInspection() {
}
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) {
// 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
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
}
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
// 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)
)
// 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
// 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 {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -25,9 +14,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
@@ -35,39 +23,37 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class CanBePrimaryConstructorPropertyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
if (property.isLocal) return
if (property.getter != null || property.setter != null || property.delegate != null) return
val assigned = property.initializer as? KtReferenceExpression ?: return
return propertyVisitor(fun(property) {
if (property.isLocal) return
if (property.getter != null || property.setter != null || property.delegate != null) return
val assigned = property.initializer as? KtReferenceExpression ?: return
val context = property.analyzeFully()
val assignedDescriptor = context.get(BindingContext.REFERENCE_TARGET, assigned) as? ValueParameterDescriptor ?: return
val context = property.analyzeFully()
val assignedDescriptor = context.get(BindingContext.REFERENCE_TARGET, assigned) as? ValueParameterDescriptor ?: return
val containingConstructor = assignedDescriptor.containingDeclaration as? ClassConstructorDescriptor ?: return
if (containingConstructor.containingDeclaration.isData) return
val containingConstructor = assignedDescriptor.containingDeclaration as? ClassConstructorDescriptor ?: return
if (containingConstructor.containingDeclaration.isData) return
val propertyTypeReference = property.typeReference
val propertyType = context.get(BindingContext.TYPE, propertyTypeReference)
if (propertyType != null && propertyType != assignedDescriptor.type) return
val propertyTypeReference = property.typeReference
val propertyType = context.get(BindingContext.TYPE, propertyTypeReference)
if (propertyType != null && propertyType != assignedDescriptor.type) return
val nameIdentifier = property.nameIdentifier ?: return
if (nameIdentifier.text != assignedDescriptor.name.asString()) return
val nameIdentifier = property.nameIdentifier ?: return
if (nameIdentifier.text != assignedDescriptor.name.asString()) return
val assignedParameter = DescriptorToSourceUtils.descriptorToDeclaration(assignedDescriptor) as? KtParameter ?: return
if (property.containingClassOrObject !== assignedParameter.containingClassOrObject) return
val assignedParameter = DescriptorToSourceUtils.descriptorToDeclaration(assignedDescriptor) as? KtParameter ?: return
if (property.containingClassOrObject !== assignedParameter.containingClassOrObject) return
if (property.containingClassOrObject?.isInterfaceClass() ?: false) return
if (property.containingClassOrObject?.isInterfaceClass() ?: false) return
holder.registerProblem(holder.manager.createProblemDescriptor(
nameIdentifier,
nameIdentifier,
"Property is explicitly assigned to parameter ${assignedDescriptor.name}, can be declared directly in constructor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
MovePropertyToConstructorIntention()
))
}
}
holder.registerProblem(holder.manager.createProblemDescriptor(
nameIdentifier,
nameIdentifier,
"Property is explicitly assigned to parameter ${assignedDescriptor.name}, can be declared directly in constructor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
MovePropertyToConstructorIntention()
))
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -21,8 +10,8 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
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.isOneLiner
import org.jetbrains.kotlin.idea.intentions.branches
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -31,45 +20,41 @@ import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
class CascadeIfInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitIfExpression(expression: KtIfExpression) {
super.visitIfExpression(expression)
ifExpressionVisitor(fun(expression) {
val branches = expression.branches
if (branches.size <= 2) return
if (expression.isOneLiner()) return
val branches = expression.branches
if (branches.size <= 2) return
if (expression.isOneLiner()) return
if (branches.any {
it == null ||
it.lastBlockStatementOrThis() is KtIfExpression
}) return
if (branches.any {
it == null ||
it.lastBlockStatementOrThis() is KtIfExpression
}) return
if (expression.isElseIf()) return
if (expression.isElseIf()) return
if (expression.anyDescendantOfType<KtExpressionWithLabel> {
it is KtBreakExpression || it is KtContinueExpression
}) return
if (expression.anyDescendantOfType<KtExpressionWithLabel> {
it is KtBreakExpression || it is KtContinueExpression
}) return
var current: KtIfExpression? = expression
while (current != null) {
val condition = current.condition
when (condition) {
is KtBinaryExpression -> when (condition.operationToken) {
KtTokens.ANDAND, KtTokens.OROR -> return
}
is KtUnaryExpression -> when (condition.operationToken) {
KtTokens.EXCL -> return
}
}
current = current.`else` as? KtIfExpression
var current: KtIfExpression? = expression
while (current != null) {
val condition = current.condition
when (condition) {
is KtBinaryExpression -> when (condition.operationToken) {
KtTokens.ANDAND, KtTokens.OROR -> return
}
is KtUnaryExpression -> when (condition.operationToken) {
KtTokens.EXCL -> return
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.FileModificationService
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.diagnostic.Logger
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.idea.caches.resolve.analyze
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.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.imports.importableFqName
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 resolutionFacade = file.getResolutionFacade()
return object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
super.visitProperty(property)
return propertyVisitor(fun(property: KtProperty) {
if (property.receiverTypeReference != null) {
val nameElement = property.nameIdentifier ?: return
val propertyDescriptor = property.resolveToDescriptorIfAny() as? PropertyDescriptor ?: return
if (property.receiverTypeReference != null) {
val nameElement = property.nameIdentifier ?: return
val propertyDescriptor = property.resolveToDescriptorIfAny() as? PropertyDescriptor ?: return
val syntheticScopes = resolutionFacade.frontendService<SyntheticScopes>()
val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return
val syntheticScopes = resolutionFacade.frontendService<SyntheticScopes>()
val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return
// don't report on hidden declarations
if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(propertyDescriptor)) return
// don't report on hidden declarations
if (resolutionFacade.frontendService<DeprecationResolver>().isHiddenInResolution(propertyDescriptor)) return
val fixes = createFixes(property, conflictingExtension, isOnTheFly)
val fixes = createFixes(property, conflictingExtension, isOnTheFly)
val problemDescriptor = holder.manager.createProblemDescriptor(
nameElement,
"This property conflicts with synthetic extension and should be removed or renamed to avoid breaking code by future changes in the compiler",
true,
fixes,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
holder.registerProblem(problemDescriptor)
}
val problemDescriptor = holder.manager.createProblemDescriptor(
nameElement,
"This property conflicts with synthetic extension and should be removed or renamed to avoid breaking code by future changes in the compiler",
true,
fixes,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
holder.registerProblem(problemDescriptor)
}
}
})
}
private fun conflictingSyntheticExtension(descriptor: PropertyDescriptor, scopes: SyntheticScopes): SyntheticJavaPropertyDescriptor? {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -37,31 +26,27 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class ConstantConditionIfInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitIfExpression(expression: KtIfExpression) {
super.visitIfExpression(expression)
return ifExpressionVisitor(fun(expression) {
val condition = expression.condition ?: return
val condition = expression.condition ?: return
val context = condition.analyze(BodyResolveMode.PARTIAL)
val constantValue = condition.constantBooleanValue(context) ?: return
val context = condition.analyze(BodyResolveMode.PARTIAL)
val constantValue = condition.constantBooleanValue(context) ?: return
val fixes = mutableListOf<LocalQuickFix>()
val fixes = mutableListOf<LocalQuickFix>()
if (expression.branch(constantValue) != null) {
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 (expression.branch(constantValue) != null) {
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())
})
}
private class SimplifyFix(
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -23,9 +12,8 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention
import org.jetbrains.kotlin.psi.KtCallExpression
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
@@ -35,29 +23,25 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class CopyWithoutNamedArgumentsInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
return callExpressionVisitor(fun(expression) {
val reference = expression.referenceExpression() as? KtNameReferenceExpression ?: return
if (reference.getReferencedNameAsName() != DataClassDescriptorResolver.COPY_METHOD_NAME) return
if (expression.valueArguments.all { it.isNamed() }) return
val reference = expression.referenceExpression() as? KtNameReferenceExpression ?: return
if (reference.getReferencedNameAsName() != DataClassDescriptorResolver.COPY_METHOD_NAME) return
if (expression.valueArguments.all { it.isNamed() }) return
val context = expression.analyze(BodyResolveMode.PARTIAL)
val call = expression.getResolvedCall(context) ?: return
val receiver = call.dispatchReceiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL)
val call = expression.getResolvedCall(context) ?: return
val receiver = call.dispatchReceiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor ?: return
if (!receiver.isData) return
if (call.candidateDescriptor != context[BindingContext.DATA_CLASS_COPY_FUNCTION, receiver]) return
if (!receiver.isData) return
if (call.candidateDescriptor != context[BindingContext.DATA_CLASS_COPY_FUNCTION, receiver]) return
holder.registerProblem(
expression.calleeExpression ?: return,
"'copy' method of data class is called without named arguments",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(AddNamesToCallArgumentsIntention(), expression.containingKtFile)
)
}
}
holder.registerProblem(
expression.calleeExpression ?: return,
"'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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -20,29 +9,24 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.primaryConstructorVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
class DataClassPrivateConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) {
super.visitPrimaryConstructor(constructor)
return primaryConstructorVisitor { constructor ->
if (constructor.containingClass()?.isData() == true && constructor.isPrivate()) {
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()) {
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)
}
holder.registerProblem(problemDescriptor)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -21,42 +10,37 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.destructuringDeclarationVisitor
class DestructuringWrongNameInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
super.visitDestructuringDeclaration(destructuringDeclaration)
return destructuringDeclarationVisitor(fun(destructuringDeclaration) {
val initializer = destructuringDeclaration.initializer ?: return
val type = initializer.analyze().getType(initializer) ?: return
val initializer = destructuringDeclaration.initializer ?: return
val type = initializer.analyze().getType(initializer) ?: return
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: 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
.firstOrNull { it.isPrimary }
?.valueParameters
?.map { it.name.asString() } ?: return
destructuringDeclaration.entries.forEachIndexed { entryIndex, entry ->
val variableName = entry.name
if (variableName != primaryParameterNames.getOrNull(entryIndex)) {
for ((parameterIndex, parameterName) in primaryParameterNames.withIndex()) {
if (parameterIndex == entryIndex) continue
if (variableName == parameterName) {
val fix = primaryParameterNames.getOrNull(entryIndex)?.let { RenameElementFix(entry, it) }
holder.registerProblem(
entry,
"Variable name '$variableName' matches the name of a different component",
*listOfNotNull(fix).toTypedArray())
break
}
destructuringDeclaration.entries.forEachIndexed { entryIndex, entry ->
val variableName = entry.name
if (variableName != primaryParameterNames.getOrNull(entryIndex)) {
for ((parameterIndex, parameterName) in primaryParameterNames.withIndex()) {
if (parameterIndex == entryIndex) continue
if (variableName == parameterName) {
val fix = primaryParameterNames.getOrNull(entryIndex)?.let { RenameElementFix(entry, it) }
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -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.findDeclaredHashCode
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.source.getPsi
@@ -71,35 +57,33 @@ sealed class GenerateEqualsOrHashCodeFix : LocalQuickFix {
class EqualsOrHashCodeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object: KtVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
val nameIdentifier = classOrObject.nameIdentifier ?: return
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
val hasEquals = classDescriptor.findDeclaredEquals(false) != null
val hasHashCode = classDescriptor.findDeclaredHashCode(false) != null
if (!hasEquals && !hasHashCode) return
return classOrObjectVisitor(fun(classOrObject) {
val nameIdentifier = classOrObject.nameIdentifier ?: return
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
val hasEquals = classDescriptor.findDeclaredEquals(false) != null
val hasHashCode = classDescriptor.findDeclaredHashCode(false) != null
if (!hasEquals && !hasHashCode) return
when (classDescriptor.kind) {
ClassKind.OBJECT -> {
if (classOrObject.superTypeListEntries.isNotEmpty()) return
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
when (classDescriptor.kind) {
ClassKind.OBJECT -> {
if (classOrObject.superTypeListEntries.isNotEmpty()) return
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
}
}
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -38,29 +27,25 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
return dotQualifiedExpressionVisitor(fun(expression) {
val callExpression = expression.callExpression ?: return
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 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 resolvedCall = expression.getResolvedCall(context) ?: return
val descriptor = resolvedCall.resultingDescriptor as? JavaMethodDescriptor ?: return
val fqName = descriptor.importableFqName?.asString() ?: return
if (!canReplaceWithStdLib(expression, fqName, args)) return
val resolvedCall = expression.getResolvedCall(context) ?: return
val descriptor = resolvedCall.resultingDescriptor as? JavaMethodDescriptor ?: return
val fqName = descriptor.importableFqName?.asString() ?: return
if (!canReplaceWithStdLib(expression, fqName, args)) return
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))
}
}
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 {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -23,30 +12,28 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
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.types.typeUtil.isBoolean
class KotlinDoubleNegationInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitPrefixExpression(expression: KtPrefixExpression) {
if (expression.operationToken != KtTokens.EXCL ||
expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true) {
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())
}
prefixExpressionVisitor(fun(expression) {
if (expression.operationToken != KtTokens.EXCL ||
expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true) {
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())
}
})
private class DoubleNegationFix : LocalQuickFix {
override fun getName() = "Remove redundant negations"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -29,52 +18,49 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
val funKeyword = function.funKeyword ?: return
val modifierList = function.modifierList ?: return
if (!modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return
if (function.annotationEntries.isNotEmpty()) return
if (function.containingClass()?.isData() == true) return
namedFunctionVisitor(fun(function) {
val funKeyword = function.funKeyword ?: return
val modifierList = function.modifierList ?: return
if (!modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return
if (function.annotationEntries.isNotEmpty()) return
if (function.containingClass()?.isData() == true) return
val bodyExpression = function.bodyExpression ?: return
val qualifiedExpression = when (bodyExpression) {
is KtDotQualifiedExpression -> bodyExpression
is KtBlockExpression -> {
val body = bodyExpression.statements.singleOrNull()
when (body) {
is KtReturnExpression -> body.returnedExpression
is KtDotQualifiedExpression -> body.takeIf {
function.typeReference.let { it == null || it.text == "Unit" }
}
else -> null
val bodyExpression = function.bodyExpression ?: return
val qualifiedExpression = when (bodyExpression) {
is KtDotQualifiedExpression -> bodyExpression
is KtBlockExpression -> {
val body = bodyExpression.statements.singleOrNull()
when (body) {
is KtReturnExpression -> body.returnedExpression
is KtDotQualifiedExpression -> body.takeIf {
function.typeReference.let { it == null || it.text == "Unit" }
}
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
if (!isSameFunctionName(superCallElement, function)) return
if (!isSameArguments(superCallElement, function)) return
if (function.isDefinedInDelegatedSuperType(qualifiedExpression)) return
val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return
if (superExpression.superTypeQualifier != null) return
val descriptor = holder.manager.createProblemDescriptor(
function,
TextRange(modifierList.startOffsetInParent, funKeyword.endOffset - function.startOffset),
"Redundant override",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
RedundantOverrideFix()
)
holder.registerProblem(descriptor)
}
}
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return
if (!isSameFunctionName(superCallElement, function)) return
if (!isSameArguments(superCallElement, function)) return
if (function.isDefinedInDelegatedSuperType(qualifiedExpression)) return
val descriptor = holder.manager.createProblemDescriptor(
function,
TextRange(modifierList.startOffsetInParent, funKeyword.endOffset - function.startOffset),
"Redundant override",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
RedundantOverrideFix()
)
holder.registerProblem(descriptor)
})
private fun isSameArguments(superCallElement: KtCallElement, function: KtNamedFunction): Boolean {
val arguments = superCallElement.valueArguments
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -28,58 +17,59 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
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.resolve.BindingContext.LEAKING_THIS
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class LeakingThisInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitExpression(expression: KtExpression) {
val context = expression.analyzeFully()
val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return
val description = when (leakingThisDescriptor) {
is NonFinalClass ->
if (expression is KtThisExpression)
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
else
return // Not supported yet
is NonFinalProperty ->
"Accessing non-final property ${leakingThisDescriptor.property.name} in constructor"
is NonFinalFunction ->
"Calling non-final function ${leakingThisDescriptor.function.name} in constructor"
else -> return // Not supported yet
return expressionVisitor { expression ->
val context = expression.analyzeFully()
val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return@expressionVisitor
val description = when (leakingThisDescriptor) {
is NonFinalClass ->
if (expression is KtThisExpression)
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
else
return@expressionVisitor // Not supported yet
is NonFinalProperty ->
"Accessing non-final property ${leakingThisDescriptor.property.name} in constructor"
is NonFinalFunction ->
"Calling non-final function ${leakingThisDescriptor.function.name} in constructor"
else -> return@expressionVisitor // 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) {
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)
else null
}
val klass = leakingThisDescriptor.classOrObject as? KtClass
val classFix =
if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) {
createMakeFinalFix(klass)
}
else null
}
val klass = leakingThisDescriptor.classOrObject as? KtClass
val classFix =
if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) {
createMakeFinalFix(klass)
}
else null
holder.registerProblem(
expression, description,
when (leakingThisDescriptor) {
is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING
else -> WEAK_WARNING
},
*(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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -27,7 +16,7 @@ import org.jetbrains.kotlin.idea.quickfix.AddConstModifierFix
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtObjectDeclaration
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.ErrorValue
@@ -48,21 +37,18 @@ class MayBeConstantInspection : AbstractKotlinInspection() {
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
super.visitProperty(property)
val status = property.getStatus()
when (status) {
NONE, JVM_FIELD_MIGHT_BE_CONST_NO_INITIALIZER,
MIGHT_BE_CONST_ERRONEOUS, JVM_FIELD_MIGHT_BE_CONST_ERRONEOUS -> return
MIGHT_BE_CONST, JVM_FIELD_MIGHT_BE_CONST -> {
holder.registerProblem(
property.nameIdentifier ?: property,
if (status == JVM_FIELD_MIGHT_BE_CONST) "'const' might be used instead of '@JvmField'" else "Might be 'const'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(AddConstModifierFix(property), property.containingFile)
)
}
return propertyVisitor { property ->
val status = property.getStatus()
when (status) {
NONE, JVM_FIELD_MIGHT_BE_CONST_NO_INITIALIZER,
MIGHT_BE_CONST_ERRONEOUS, JVM_FIELD_MIGHT_BE_CONST_ERRONEOUS -> return@propertyVisitor
MIGHT_BE_CONST, JVM_FIELD_MIGHT_BE_CONST -> {
holder.registerProblem(
property.nameIdentifier ?: property,
if (status == JVM_FIELD_MIGHT_BE_CONST) "'const' might be used instead of '@JvmField'" else "Might be 'const'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
IntentionWrapper(AddConstModifierFix(property), property.containingFile)
)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -25,37 +14,34 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors.*
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.lazy.BodyResolveMode
class MigrateDiagnosticSuppressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
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) {
super.visitAnnotationEntry(annotationEntry)
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
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
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)
)
}
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 {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -28,41 +17,39 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid
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.getParentResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
val callableReference = lambdaExpression.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression
if (callableReference != null) {
val context = lambdaExpression.analyze()
val parentResolvedCall = lambdaExpression.getParentResolvedCall(context)
if (parentResolvedCall != null) {
val originalParameterDescriptor =
parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original
if (originalParameterDescriptor != null) {
val expectedType = originalParameterDescriptor.type
if (expectedType.isBuiltinFunctionalType) {
val returnType = expectedType.getReturnTypeFromFunctionType()
if (returnType.isBuiltinFunctionalTypeOrSubtype) return
}
return lambdaExpressionVisitor(fun(lambdaExpression) {
val callableReference = lambdaExpression.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression
if (callableReference != null) {
val context = lambdaExpression.analyze()
val parentResolvedCall = lambdaExpression.getParentResolvedCall(context)
if (parentResolvedCall != null) {
val originalParameterDescriptor =
parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original
if (originalParameterDescriptor != null) {
val expectedType = originalParameterDescriptor.type
if (expectedType.isBuiltinFunctionalType) {
val returnType = expectedType.getReturnTypeFromFunctionType()
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(
@@ -123,11 +123,7 @@ class EnumEntryNameInspection : NamingConventionInspection(
START_UPPER, NO_BAD_CHARACTERS_OR_UNDERSCORE
) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitEnumEntry(enumEntry: KtEnumEntry) {
verifyName(enumEntry, holder)
}
}
return enumEntryVisitor { enumEntry -> verifyName(enumEntry, holder) }
}
}
@@ -137,11 +133,8 @@ class FunctionNameInspection : NamingConventionInspection(
START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS
) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
if (TestUtils.isInTestSourceContent(function)) {
return
}
return namedFunctionVisitor { function ->
if (!TestUtils.isInTestSourceContent(function)) {
verifyName(function, holder)
}
}
@@ -154,16 +147,14 @@ class TestFunctionNameInspection : NamingConventionInspection(
START_LOWER
) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
if (!TestUtils.isInTestSourceContent(function)) {
return
}
if (function.nameIdentifier?.text?.startsWith("`") == true) {
return
}
verifyName(function, holder)
return namedFunctionVisitor { function ->
if (!TestUtils.isInTestSourceContent(function)) {
return@namedFunctionVisitor
}
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 }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
if (property.getKind() == kind) {
verifyName(property, holder)
}
return propertyVisitor { property ->
if (property.getKind() == kind) {
verifyName(property, holder)
}
}
}
@@ -234,17 +223,15 @@ class LocalVariableNameInspection :
class PackageNameInspection :
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 {
return object : KtVisitorVoid() {
override fun visitPackageDirective(directive: KtPackageDirective) {
val qualifiedName = directive.qualifiedName
if (qualifiedName.isNotEmpty() && nameRegex?.matches(qualifiedName) == false) {
val message = getNameMismatchMessage(qualifiedName)
holder.registerProblem(
directive.packageNameExpression!!,
"Package name <code>#ref</code> $message #loc",
RenamePackageFix()
)
}
return packageDirectiveVisitor { directive ->
val qualifiedName = directive.qualifiedName
if (qualifiedName.isNotEmpty() && nameRegex?.matches(qualifiedName) == false) {
val message = getNameMismatchMessage(qualifiedName)
holder.registerProblem(
directive.packageNameExpression!!,
"Package name <code>#ref</code> $message #loc",
RenamePackageFix()
)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -31,14 +20,12 @@ import org.jetbrains.kotlin.types.TypeUtils
class NullChecksToSafeCallInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitBinaryExpression(expression: KtBinaryExpression) {
if (isNullChecksToSafeCallFixAvailable(expression)) {
holder.registerProblem(expression,
"Null-checks replaceable with safe-calls",
ProblemHighlightType.WEAK_WARNING,
NullChecksToSafeCallCheckFix())
}
binaryExpressionVisitor { expression ->
if (isNullChecksToSafeCallFixAvailable(expression)) {
holder.registerProblem(expression,
"Null-checks replaceable with safe-calls",
ProblemHighlightType.WEAK_WARNING,
NullChecksToSafeCallCheckFix())
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -34,36 +23,34 @@ import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
class NullableBooleanElvisInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitBinaryExpression(expression: KtBinaryExpression) {
if (expression.operationToken != KtTokens.ELVIS) return
val lhs = expression.left ?: return
val rhs = expression.right ?: return
if (!KtPsiUtil.isBooleanConstant(rhs)) return
val lhsType = lhs.analyze(BodyResolveMode.PARTIAL).getType(lhs) ?: return
if (TypeUtils.isNullableType(lhsType) && lhsType.isBooleanOrNullableBoolean()) {
val parentIfOrWhile = PsiTreeUtil.getParentOfType(
expression, KtIfExpression::class.java, KtWhileExpressionBase::class.java)
val condition = when (parentIfOrWhile) {
is KtIfExpression -> parentIfOrWhile.condition
is KtWhileExpressionBase -> parentIfOrWhile.condition
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()
)
}
binaryExpressionVisitor(fun(expression) {
if (expression.operationToken != KtTokens.ELVIS) return
val lhs = expression.left ?: return
val rhs = expression.right ?: return
if (!KtPsiUtil.isBooleanConstant(rhs)) return
val lhsType = lhs.analyze(BodyResolveMode.PARTIAL).getType(lhs) ?: return
if (TypeUtils.isNullableType(lhsType) && lhsType.isBooleanOrNullableBoolean()) {
val parentIfOrWhile = PsiTreeUtil.getParentOfType(
expression, KtIfExpression::class.java, KtWhileExpressionBase::class.java)
val condition = when (parentIfOrWhile) {
is KtIfExpression -> parentIfOrWhile.condition
is KtWhileExpressionBase -> parentIfOrWhile.condition
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()
)
}
})
private class ReplaceWithEqualityCheckFix : LocalQuickFix {
override fun getName() = "Replace with equality check"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -24,33 +13,30 @@ import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtVisitorVoid
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.visibilityModifier
class ProtectedInFinalInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
val visibilityModifier = declaration.visibilityModifier() ?: return
val modifierType = visibilityModifier.node?.elementType
if (modifierType == KtTokens.PROTECTED_KEYWORD) {
val parentClass = declaration.getParentOfType<KtClass>(true) ?: return
if (!parentClass.isInheritable() && !parentClass.isEnum() &&
declaration.implicitVisibility() != KtTokens.PROTECTED_KEYWORD) {
holder.registerProblem(visibilityModifier,
"'protected' visibility is effectively 'private' in a final class",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
MakePrivateFix(),
MakeOpenFix()
)
}
return declarationVisitor(fun(declaration) {
val visibilityModifier = declaration.visibilityModifier() ?: return
val modifierType = visibilityModifier.node?.elementType
if (modifierType == KtTokens.PROTECTED_KEYWORD) {
val parentClass = declaration.getParentOfType<KtClass>(true) ?: return
if (!parentClass.isInheritable() && !parentClass.isEnum() &&
declaration.implicitVisibility() != KtTokens.PROTECTED_KEYWORD) {
holder.registerProblem(visibilityModifier,
"'protected' visibility is effectively 'private' in a final class",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
MakePrivateFix(),
MakeOpenFix()
)
}
}
}
})
}
class MakePrivateFix : LocalQuickFix {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -35,20 +24,17 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
class RecursivePropertyAccessorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
super.visitSimpleNameExpression(expression)
if (isRecursivePropertyAccess(expression)) {
holder.registerProblem(expression,
"Recursive property accessor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithFieldFix())
}
else if (isRecursiveSyntheticPropertyAccess(expression)) {
holder.registerProblem(expression,
"Recursive synthetic property accessor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return simpleNameExpressionVisitor { expression ->
if (isRecursivePropertyAccess(expression)) {
holder.registerProblem(expression,
"Recursive property accessor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithFieldFix())
}
else if (isRecursiveSyntheticPropertyAccess(expression)) {
holder.registerProblem(expression,
"Recursive synthetic property accessor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -29,61 +18,57 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RedundantExplicitTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
super.visitProperty(property)
propertyVisitor(fun(property) {
if (!property.isLocal) return
val typeReference = property.typeReference ?: return
val initializer = property.initializer ?: return
if (!property.isLocal) return
val typeReference = property.typeReference ?: return
val initializer = property.initializer ?: return
val type = property.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return
when (initializer) {
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
val type = property.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return
when (initializer) {
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
}
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
}
holder.registerProblem(
typeReference,
"Explicitly given type is redundant here",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), property.containingKtFile)
)
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
}
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -20,19 +9,15 @@ import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class RedundantGetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitPropertyAccessor(accessor: KtPropertyAccessor) {
super.visitPropertyAccessor(accessor)
if (accessor.isRedundantGetter()) {
holder.registerProblem(accessor,
"Redundant getter",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantGetterFix())
}
return propertyAccessorVisitor { accessor ->
if (accessor.isRedundantGetter()) {
holder.registerProblem(accessor,
"Redundant getter",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantGetterFix())
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -27,18 +16,15 @@ import org.jetbrains.kotlin.psi.*
class RedundantIfInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitIfExpression(expression: KtIfExpression) {
super.visitIfExpression(expression)
if (expression.condition == null) return
val (redundancyType, branchType) = RedundancyType.of(expression)
if (redundancyType == RedundancyType.NONE) return
return ifExpressionVisitor { expression ->
if (expression.condition == null) return@ifExpressionVisitor
val (redundancyType, branchType) = RedundancyType.of(expression)
if (redundancyType == RedundancyType.NONE) return@ifExpressionVisitor
holder.registerProblem(expression,
"Redundant 'if' statement",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantIf(redundancyType, branchType))
}
holder.registerProblem(expression,
"Redundant 'if' statement",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantIf(redundancyType, branchType))
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -23,23 +12,20 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.lambdaExpressionVisitor
class RedundantLambdaArrowInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
val functionLiteral = lambdaExpression.functionLiteral
if (functionLiteral.valueParameters.isNotEmpty()) return
val arrow = functionLiteral.arrow ?: return
return lambdaExpressionVisitor { lambdaExpression ->
val functionLiteral = lambdaExpression.functionLiteral
if (functionLiteral.valueParameters.isNotEmpty()) return@lambdaExpressionVisitor
val arrow = functionLiteral.arrow ?: return@lambdaExpressionVisitor
holder.registerProblem(
arrow,
"Redundant lambda arrow",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
DeleteFix())
}
holder.registerProblem(
arrow,
"Redundant lambda arrow",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
DeleteFix())
}
}
@@ -1,53 +1,31 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.*
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.mapModality
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.declarationVisitor
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
val modalityModifier = declaration.modalityModifier() ?: return
val modalityModifierType = modalityModifier.node.elementType
val implicitModality = declaration.implicitModality()
return declarationVisitor { declaration ->
val modalityModifier = declaration.modalityModifier() ?: return@declarationVisitor
val modalityModifierType = modalityModifier.node.elementType
val implicitModality = declaration.implicitModality()
if (modalityModifierType != implicitModality) return
if (modalityModifierType != implicitModality) return@declarationVisitor
holder.registerProblem(modalityModifier,
"Redundant modality modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(declaration, implicitModality, isRedundant = true),
declaration.containingFile))
}
holder.registerProblem(modalityModifier,
"Redundant modality modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(declaration, implicitModality, isRedundant = true),
declaration.containingFile))
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -53,63 +42,60 @@ import org.jetbrains.kotlin.utils.keysToMapExceptNulls
class RedundantSamConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
private fun createQuickFix(expression: KtCallExpression): LocalQuickFix {
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructor"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(expression)) return
replaceSamConstructorCall(expression)
}
}
return callExpressionVisitor(fun(expression) {
if (expression.valueArguments.isEmpty()) return
val samConstructorCalls = samConstructorCallsToBeConverted(expression)
if (samConstructorCalls.isEmpty()) return
val single = samConstructorCalls.singleOrNull()
if (single != null) {
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 {
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)
}
}
}
holder.registerProblem(problemDescriptor)
}
})
}
override fun visitCallExpression(expression: KtCallExpression) {
if (expression.valueArguments.isEmpty()) return
val samConstructorCalls = samConstructorCallsToBeConverted(expression)
if (samConstructorCalls.isEmpty()) return
val single = samConstructorCalls.singleOrNull()
if (single != null) {
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(expression: KtCallExpression): LocalQuickFix {
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructor"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(expression)) return
replaceSamConstructorCall(expression)
}
}
}
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 {
fun replaceSamConstructorCall(callExpression: KtCallExpression): KtLambdaExpression {
val functionalArgument = callExpression.samConstructorValueArgument()?.getArgumentExpression()
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -25,15 +14,12 @@ import org.jetbrains.kotlin.psi.*
class RedundantSetterInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitPropertyAccessor(accessor: KtPropertyAccessor) {
super.visitPropertyAccessor(accessor)
if (accessor.isRedundantSetter()) {
holder.registerProblem(accessor,
"Redundant setter",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantSetterFix())
}
return propertyAccessorVisitor { accessor ->
if (accessor.isRedundantSetter()) {
holder.registerProblem(accessor,
"Redundant setter",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveRedundantSetterFix())
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -24,43 +13,37 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality
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.project.languageVersionSettings
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.namedFunctionVisitor
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.resolve.BindingContext
class RedundantSuspendModifierInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
if (!function.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) return
return namedFunctionVisitor(fun(function) {
if (!function.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) return
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
if (!function.hasBody()) return
if (function.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
if (!function.hasBody()) return
if (function.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
val context = function.analyzeFully()
val descriptor = context[BindingContext.FUNCTION, function] ?: 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))
val context = function.analyzeFully()
val descriptor = context[BindingContext.FUNCTION, function] ?: 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))
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -25,27 +14,22 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitReferenceExpression(expression: KtReferenceExpression) {
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())
return referenceExpressionVisitor(fun(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())
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -24,34 +13,28 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.namedFunctionVisitor
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.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
class RedundantUnitReturnTypeInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
if (function.containingFile is KtCodeFragment) return
val typeElement = function.typeReference?.typeElement ?: return
val context = function.analyze(BodyResolveMode.PARTIAL)
val descriptor = context[BindingContext.FUNCTION, function] ?: return
if (descriptor.returnType?.isUnit() == true) {
if (!function.hasBlockBody()) {
return
}
holder.registerProblem(typeElement,
"Redundant 'Unit' return type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), function.containingKtFile))
return namedFunctionVisitor(fun(function) {
if (function.containingFile is KtCodeFragment) return
val typeElement = function.typeReference?.typeElement ?: return
val context = function.analyze(BodyResolveMode.PARTIAL)
val descriptor = context[BindingContext.FUNCTION, function] ?: return
if (descriptor.returnType?.isUnit() == true) {
if (!function.hasBlockBody()) {
return
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -20,23 +9,20 @@ import com.intellij.codeInspection.*
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.declarationVisitor
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
class RedundantVisibilityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
val visibilityModifier = declaration.visibilityModifier() ?: return
val implicitVisibility = declaration.implicitVisibility()
if (visibilityModifier.node.elementType == implicitVisibility) {
holder.registerProblem(visibilityModifier,
"Redundant visibility modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(declaration, implicitVisibility, isRedundant = true),
declaration.containingFile))
}
return declarationVisitor { declaration ->
val visibilityModifier = declaration.visibilityModifier() ?: return@declarationVisitor
val implicitVisibility = declaration.implicitVisibility()
if (visibilityModifier.node.elementType == implicitVisibility) {
holder.registerProblem(visibilityModifier,
"Redundant visibility modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveModifierFix(declaration, implicitVisibility, isRedundant = true),
declaration.containingFile))
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -31,36 +20,32 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
class RemoveRedundantSpreadOperatorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitArgument(argument: KtValueArgument) {
super.visitArgument(argument)
val spreadElement = argument.getSpreadElement() ?: return
if (argument.isNamed()) return
val argumentExpression = argument.getArgumentExpression() ?: return
val argumentOffset = argument.startOffset
val startOffset = spreadElement.startOffset - argumentOffset
val endOffset =
when (argumentExpression) {
is KtCallExpression -> {
if (!argumentExpression.isArrayOfMethod()) return
argumentExpression.calleeExpression!!.endOffset - argumentOffset
}
is KtCollectionLiteralExpression -> startOffset + 1
else -> return
return valueArgumentVisitor(fun(argument) {
val spreadElement = argument.getSpreadElement() ?: return
if (argument.isNamed()) return
val argumentExpression = argument.getArgumentExpression() ?: return
val argumentOffset = argument.startOffset
val startOffset = spreadElement.startOffset - argumentOffset
val endOffset =
when (argumentExpression) {
is KtCallExpression -> {
if (!argumentExpression.isArrayOfMethod()) return
argumentExpression.calleeExpression!!.endOffset - argumentOffset
}
is KtCollectionLiteralExpression -> startOffset + 1
else -> return
}
val problemDescriptor = holder.manager.createProblemDescriptor(
argument,
TextRange(startOffset, endOffset),
"Remove redundant spread operator",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
RemoveRedundantSpreadOperatorQuickfix()
)
holder.registerProblem(problemDescriptor)
}
}
val problemDescriptor = holder.manager.createProblemDescriptor(
argument,
TextRange(startOffset, endOffset),
"Remove redundant spread operator",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
RemoveRedundantSpreadOperatorQuickfix()
)
holder.registerProblem(problemDescriptor)
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -22,20 +11,16 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention
import org.jetbrains.kotlin.idea.intentions.isSetterParameter
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.parameterVisitor
class RemoveSetterParameterTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(dcl: KtDeclaration) {
val typeReference = (dcl as? KtParameter)?.takeIf { it.isSetterParameter }?.typeReference ?: return
holder.registerProblem(typeReference,
"Redundant setter parameter type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), dcl.containingKtFile))
}
return parameterVisitor { dcl ->
val typeReference = dcl.takeIf { it.isSetterParameter }?.typeReference ?: return@parameterVisitor
holder.registerProblem(typeReference,
"Redundant setter parameter type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(RemoveExplicitTypeIntention(), dcl.containingKtFile))
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -29,27 +18,23 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RemoveToStringInStringTemplateInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
dotQualifiedExpressionVisitor(fun(expression) {
if (expression.parent !is KtBlockStringTemplateEntry) return
if (expression.receiverExpression is KtSuperExpression) return
val selectorExpression = expression.selectorExpression ?: return
if (!expression.isToString()) return
if (expression.parent !is KtBlockStringTemplateEntry) return
if (expression.receiverExpression is KtSuperExpression) return
val selectorExpression = expression.selectorExpression ?: return
if (!expression.isToString()) return
holder.registerProblem(selectorExpression,
"Redundant 'toString()' call in string template",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveToStringFix())
})
}
holder.registerProblem(selectorExpression,
"Redundant 'toString()' call in string template",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveToStringFix())
}
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" }
}
}
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 {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -32,39 +21,35 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class ReplaceArrayOfWithLiteralInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
return callExpressionVisitor(fun(expression) {
if (!expression.languageVersionSettings.supportsFeature(ArrayLiteralsInAnnotations) &&
!ApplicationManager.getApplication().isUnitTestMode) return
if (!expression.languageVersionSettings.supportsFeature(ArrayLiteralsInAnnotations) &&
!ApplicationManager.getApplication().isUnitTestMode) return
val calleeExpression = expression.calleeExpression as? KtNameReferenceExpression ?: return
if (!expression.isArrayOfMethod()) return
val calleeExpression = expression.calleeExpression as? KtNameReferenceExpression ?: return
if (!expression.isArrayOfMethod()) return
val parent = expression.parent
when (parent) {
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
val parent = expression.parent
when (parent) {
is KtValueArgument -> {
if (parent.parent.parent !is KtAnnotationEntry) return
if (parent.getSpreadElement() != null && !parent.isNamed()) return
}
val calleeName = calleeExpression.getReferencedName()
holder.registerProblem(
calleeExpression,
"'$calleeName' call should be replaced with array literal [...]",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceWithArrayLiteralFix()
)
is KtParameter -> {
val constructor = parent.parent.parent as? KtPrimaryConstructor ?: return
val containingClass = constructor.getContainingClassOrObject()
if (!containingClass.isAnnotation()) return
}
else -> return
}
}
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 {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -26,7 +15,10 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression
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.lazy.BodyResolveMode
@@ -34,26 +26,23 @@ class ReplaceToWithInfixFormInspection : AbstractKotlinInspection() {
private val compatibleNames = setOf("to")
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
if (expression.callExpression?.valueArguments?.size != 1) return
if (expression.calleeName !in compatibleNames) return
return dotQualifiedExpressionVisitor(fun(expression) {
if (expression.callExpression?.valueArguments?.size != 1) return
if (expression.calleeName !in compatibleNames) return
val context = expression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = expression.getResolvedCall(context) ?: return
val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = expression.getResolvedCall(context) ?: return
val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return
if (!function.isInfix) return
if (!function.isInfix) return
holder.registerProblem(
expression,
"Replace 'to' with infix form",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceToWithInfixFormQuickfix()
)
}
}
holder.registerProblem(
expression,
"Replace 'to' with infix form",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
ReplaceToWithInfixFormQuickfix()
)
})
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -31,64 +20,59 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
class SelfAssignmentInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
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) {
is KtNameReferenceExpression ->
this
is KtDotQualifiedExpression ->
(selectorExpression as? KtNameReferenceExpression)?.takeIf { receiverExpression is KtThisExpression }
else ->
null
val context = expression.analyze(BodyResolveMode.PARTIAL)
val leftResolvedCall = left.getResolvedCall(context)
val leftCallee = leftResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
val rightResolvedCall = right.getResolvedCall(context)
val rightCallee = rightResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
if (leftCallee != rightCallee) return
if (!rightCallee.isVar) return
if (rightCallee is PropertyDescriptor) {
if (rightCallee.isOverridable) return
if (rightCallee.accessors.any { !it.isDefault }) return
}
private fun KtExpression.receiverDeclarationDescriptor(
resolvedCall: ResolvedCall<out CallableDescriptor>,
context: BindingContext
): 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
if (left.receiverDeclarationDescriptor(leftResolvedCall, context) !=
right.receiverDeclarationDescriptor(rightResolvedCall, context)) {
return
}
override fun visitBinaryExpression(expression: KtBinaryExpression) {
super.visitBinaryExpression(expression)
holder.registerProblem(right,
"Variable '${rightCallee.name}' is assigned to itself",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
RemoveSelfAssignmentFix())
})
}
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) {
is KtNameReferenceExpression ->
this
is KtDotQualifiedExpression ->
(selectorExpression as? KtNameReferenceExpression)?.takeIf { receiverExpression is KtThisExpression }
else ->
null
}
val context = expression.analyze(BodyResolveMode.PARTIAL)
val leftResolvedCall = left.getResolvedCall(context)
val leftCallee = leftResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
val rightResolvedCall = right.getResolvedCall(context)
val rightCallee = rightResolvedCall?.resultingDescriptor as? VariableDescriptor ?: return
if (leftCallee != rightCallee) return
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())
}
private fun KtExpression.receiverDeclarationDescriptor(
resolvedCall: ResolvedCall<out CallableDescriptor>,
context: BindingContext
): 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
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -30,22 +19,16 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class SimplifyWhenWithBooleanConstantConditionInspection : AbstractKotlinInspection() {
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) {
super.visitWhenExpression(expression)
if (expression.closeBrace == null) return
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())
}
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -24,18 +13,16 @@ import org.jetbrains.kotlin.psi.*
class SuspiciousEqualsCombination : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitBinaryExpression(expression: KtBinaryExpression) {
if (expression.parent is KtBinaryExpression) return
val operands = expression.parseBinary()
val eqeq = operands.eqEqOperands.map { it.text }
val eqeqeq = operands.eqEqEqOperands.map { it.text }
if (eqeq.intersect(eqeqeq).isNotEmpty()) {
holder.registerProblem(expression, "Suspicious combination of == and ===",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
binaryExpressionVisitor(fun(expression) {
if (expression.parent is KtBinaryExpression) return
val operands = expression.parseBinary()
val eqeq = operands.eqEqOperands.map { it.text }
val eqeqeq = operands.eqEqEqOperands.map { it.text }
if (eqeq.intersect(eqeqeq).isNotEmpty()) {
holder.registerProblem(expression, "Suspicious combination of == and ===",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
}
})
private fun KtBinaryExpression.parseBinary(pair: ComparisonOperands = ComparisonOperands()): ComparisonOperands {
when (operationToken) {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -20,8 +9,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
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.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -30,18 +18,14 @@ import org.jetbrains.kotlin.types.isDynamic
class UnsafeCastFromDynamicInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitExpression(expression: KtExpression) {
super.visitExpression(expression)
return expressionVisitor(fun(expression) {
val context = expression.analyze(BodyResolveMode.PARTIAL)
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, expression] ?: return
val actualType = expression.getType(context) ?: return
val context = expression.analyze(BodyResolveMode.PARTIAL)
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, expression] ?: return
val actualType = expression.getType(context) ?: return
if (actualType.isDynamic() && !expectedType.isDynamic() && !TypeUtils.noExpectedType(expectedType)) {
holder.registerProblem(expression, "Implicit (unsafe) cast from dynamic to $expectedType")
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
@@ -28,7 +15,10 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
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.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -37,28 +27,26 @@ import org.jetbrains.kotlin.resolve.source.getPsi
class UnusedLambdaExpressionBodyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitCallExpression(expression: KtCallExpression) {
val context = expression.analyze(BodyResolveMode.PARTIAL)
if (expression.used(context)) {
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))
return callExpressionVisitor(fun(expression) {
val context = expression.analyze(BodyResolveMode.PARTIAL)
if (expression.used(context)) {
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))
})
}
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.
*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -139,47 +128,44 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
override fun runForWholeFile() = true
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
if (declaration !is KtNamedDeclaration) return
val message = declaration.describe()?.let { "$it is never used" } ?: return
return namedDeclarationVisitor(fun(declaration) {
val message = declaration.describe()?.let { "$it is never used" } ?: return
if (!ProjectRootsUtil.isInProjectSource(declaration)) return
if (!ProjectRootsUtil.isInProjectSource(declaration)) return
// 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)
// 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 KtEnumEntry) return
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (declaration is KtProperty && declaration.isLocal) return
if (declaration is KtParameter && (declaration.getParent().parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return
if (declaration is KtEnumEntry) return
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (declaration is KtProperty && declaration.isLocal) return
if (declaration is KtParameter && (declaration.getParent().parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return
// More expensive, resolve-based checks
val descriptor = declaration.resolveToDescriptorIfAny() ?: return
if (descriptor is FunctionDescriptor && descriptor.isOperator) return
if (isEntryPoint(declaration)) return
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) 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
if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return
// More expensive, resolve-based checks
val descriptor = declaration.resolveToDescriptorIfAny() ?: return
if (descriptor is FunctionDescriptor && descriptor.isOperator) return
if (isEntryPoint(declaration)) return
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) 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
if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return
// Main checks: finding reference usages && text usages
if (hasNonTrivialUsages(declaration, descriptor)) return
if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return
// Main checks: finding reference usages && text usages
if (hasNonTrivialUsages(declaration, descriptor)) return
if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return
val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return
val problemDescriptor = holder.manager.createProblemDescriptor(
psiElement,
null,
message,
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
true,
*createQuickFixes(declaration).toTypedArray()
)
val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return
val problemDescriptor = holder.manager.createProblemDescriptor(
psiElement,
null,
message,
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
true,
*createQuickFixes(declaration).toTypedArray()
)
holder.registerProblem(problemDescriptor)
}
}
holder.registerProblem(problemDescriptor)
})
}
override val suppressionKey: String get() = "unused"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -67,35 +56,31 @@ class UseExpressionBodyInspection(private val convertEmptyToUnit: Boolean) : Abs
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
super.visitDeclaration(declaration)
declaration as? KtDeclarationWithBody ?: return
val (toHighlightElement, suffix, highlightType) = statusFor(declaration) ?: return
// Change range to start with left brace
val hasHighlighting = highlightType != INFORMATION
val toHighlightRange = toHighlightElement?.textRange?.let {
if (hasHighlighting) {
it
}
else {
// Extend range to [left brace..end of highlight element]
val offset = (declaration.blockExpression()?.lBrace?.startOffset ?: it.startOffset) - it.startOffset
it.shiftRight(offset).grown(-offset)
}
declarationVisitor(fun(declaration) {
declaration as? KtDeclarationWithBody ?: return
val (toHighlightElement, suffix, highlightType) = statusFor(declaration) ?: return
// Change range to start with left brace
val hasHighlighting = highlightType != INFORMATION
val toHighlightRange = toHighlightElement?.textRange?.let {
if (hasHighlighting) {
it
}
else {
// 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? {
val body = blockExpression() ?: return null
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -23,24 +12,22 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
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.whenExpressionVisitor
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class WhenWithOnlyElseInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitWhenExpression(expression: KtWhenExpression) {
val singleEntry = expression.entries.singleOrNull()
if (singleEntry?.isElse != true) return
return whenExpressionVisitor { expression ->
val singleEntry = expression.entries.singleOrNull()
if (singleEntry?.isElse != true) return@whenExpressionVisitor
val usedAsExpression = expression.isUsedAsExpression(expression.analyze())
val usedAsExpression = expression.isUsedAsExpression(expression.analyze())
holder.registerProblem(expression,
"'when' has only 'else' branch and should be simplified",
SimplifyFix(usedAsExpression)
)
}
holder.registerProblem(expression,
"'when' has only 'else' branch and should be simplified",
SimplifyFix(usedAsExpression)
)
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
@@ -34,28 +23,25 @@ class WrapUnaryOperatorInspection : AbstractKotlinInspection() {
val numberTypes = listOf(KtNodeTypes.INTEGER_CONSTANT, KtNodeTypes.FLOAT_CONSTANT)
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitPrefixExpression(expression: KtPrefixExpression) {
super.visitPrefixExpression(expression)
if (expression.operationToken.isUnaryMinusOrPlus()) {
val baseExpression = expression.baseExpression
if (baseExpression is KtDotQualifiedExpression) {
val receiverExpression = baseExpression.receiverExpression
if (receiverExpression is KtConstantExpression &&
receiverExpression.node.elementType in numberTypes) {
holder.registerProblem(expression,
"Wrap unary operator and value with ()",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
WrapUnaryOperatorQuickfix())
}
return prefixExpressionVisitor { expression ->
if (expression.operationToken.isUnaryMinusOrPlus()) {
val baseExpression = expression.baseExpression
if (baseExpression is KtDotQualifiedExpression) {
val receiverExpression = baseExpression.receiverExpression
if (receiverExpression is KtConstantExpression &&
receiverExpression.node.elementType in numberTypes) {
holder.registerProblem(expression,
"Wrap unary operator and value with ()",
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 {
override fun getName() = "Wrap unary operator and value with ()"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections.collections
@@ -34,67 +23,63 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
object : KtVisitorVoid() {
override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
super.visitQualifiedExpression(expression)
qualifiedExpressionVisitor(fun(expression) {
val firstExpression = expression.receiverExpression
val firstCallExpression = getCallExpression(firstExpression) ?: return
val firstExpression = expression.receiverExpression
val firstCallExpression = getCallExpression(firstExpression) ?: return
val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: 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 secondCalleeExpression = secondCallExpression.calleeExpression ?: return
val actualConversions = conversionGroups[
firstCalleeExpression.text to secondCalleeExpression.text
] ?: return
val context = expression.analyze()
val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return
val conversion = actualConversions.firstOrNull {
firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
} ?: return
val context = expression.analyze()
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
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
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
if (firstReceiverRawType != null) {
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 on maps due to lack of relevant stdlib functions
val firstReceiverType = firstResolvedCall.extensionReceiver?.type
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
if (firstReceiverRawType != null) {
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)
})
companion object {
@@ -11,7 +11,6 @@ import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.siyeh.ig.psiutils.TestUtils
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.kdoc.psi.impl.KDocSection
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.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -34,28 +34,22 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KDocMissingDocumentationInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
KDocMissingDocumentationInspection(holder)
private class KDocMissingDocumentationInspection(private val holder: ProblemsHolder) : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
if (TestUtils.isInTestSourceContent(element)) {
return
}
if (element is KtNamedDeclaration) {
namedDeclarationVisitor { element ->
if (TestUtils.isInTestSourceContent(element)) {
return@namedDeclarationVisitor
}
val nameIdentifier = element.nameIdentifier
val descriptor = element.resolveToDescriptorIfAny(BodyResolveMode.FULL)
as? DeclarationDescriptorWithVisibility
as? MemberDescriptor ?: return
as? MemberDescriptor ?: return@namedDeclarationVisitor
if (nameIdentifier != null && descriptor.isEffectivelyPublicApi) {
if (descriptor.findKDoc { DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, it) } == null) {
val message = element.describe()?.let { "$it is missing documentation" } ?: "Missing documentation"
holder.registerProblem(nameIdentifier, message, AddDocumentationFix())
}
}
}
}
}
class AddDocumentationFix : LocalQuickFix {
override fun getName(): String = "Add documentation"