diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt new file mode 100644 index 00000000000..bf88aa507e4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt @@ -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) + } + } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractPrimitiveRangeToInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractPrimitiveRangeToInspection.kt index cd1c16d77ac..5beae14317c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractPrimitiveRangeToInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractPrimitiveRangeToInspection.kt @@ -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) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/AddVarianceModifierInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/AddVarianceModifierInspection.kt index c0a365331cf..70f7d154a6d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/AddVarianceModifierInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/AddVarianceModifierInspection.kt @@ -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" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ArrayInDataClassInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ArrayInDataClassInspection.kt index ce530b023e0..acd19c3469e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ArrayInDataClassInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ArrayInDataClassInspection.kt @@ -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()" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeParameterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeParameterInspection.kt index 52410ee6a14..c788ee40aed 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeParameterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeParameterInspection.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CanBePrimaryConstructorPropertyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBePrimaryConstructorPropertyInspection.kt index 0399cc55c07..70ca74a3a37 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/CanBePrimaryConstructorPropertyInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBePrimaryConstructorPropertyInspection.kt @@ -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() + )) + }) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt index b4de4510ef9..8215775f735 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CascadeIfInspection.kt @@ -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 { + it is KtBreakExpression || it is KtContinueExpression + }) return - if (expression.anyDescendantOfType { - 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 } -} \ No newline at end of file + + holder.registerProblem( + expression.ifKeyword, + "Cascade if should be replaced with when", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntentionWrapper(IfToWhenIntention(), expression.containingKtFile) + ) + }) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt index 8dbe2357433..e7afe1cbf63 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt @@ -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() + val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return - val syntheticScopes = resolutionFacade.frontendService() - val conflictingExtension = conflictingSyntheticExtension(propertyDescriptor, syntheticScopes) ?: return + // don't report on hidden declarations + if (resolutionFacade.frontendService().isHiddenInResolution(propertyDescriptor)) return - // don't report on hidden declarations - if (resolutionFacade.frontendService().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? { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ConstantConditionIfInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ConstantConditionIfInspection.kt index d5a84246883..a9e40eb1574 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ConstantConditionIfInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ConstantConditionIfInspection.kt @@ -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() - val fixes = mutableListOf() - - 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( diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CopyWithoutNamedArgumentsInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CopyWithoutNamedArgumentsInspection.kt index 659150ff4e2..22b650d1ddc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/CopyWithoutNamedArgumentsInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CopyWithoutNamedArgumentsInspection.kt @@ -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) + ) + }) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DataClassPrivateConstructorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DataClassPrivateConstructorInspection.kt index 32079af53c7..31484a3c12b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/DataClassPrivateConstructorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DataClassPrivateConstructorInspection.kt @@ -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) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DestructuringWrongNameInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DestructuringWrongNameInspection.kt index 284bcd75faa..e5c9125c842 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/DestructuringWrongNameInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DestructuringWrongNameInspection.kt @@ -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 } } } } - } + }) } -} \ No newline at end of file +} diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/EqualsOrHashCodeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/EqualsOrHashCodeInspection.kt index 48144e83d18..2bcbad05df3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/EqualsOrHashCodeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/EqualsOrHashCodeInspection.kt @@ -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) "equals()" else "hashCode()", - if (hasEquals) "hashCode()" else "equals()" - ) - 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) "equals()" else "hashCode()", + if (hasEquals) "hashCode()" else "equals()" + ) + holder.registerProblem( + nameIdentifier, + description, + if (hasEquals) GenerateEqualsOrHashCodeFix.HashCode else GenerateEqualsOrHashCodeFix.Equals + ) + } + else -> return } - } + }) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt index ee8d65ac711..7bee3712eb7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt @@ -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): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt index 31044cd2ab9..16369ca18f2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt @@ -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" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinRedundantOverrideInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinRedundantOverrideInspection.kt index 298b4cefe51..34a960a229c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinRedundantOverrideInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinRedundantOverrideInspection.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt index 4c361a4a228..67dfc1f7938 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt @@ -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()) + ) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MayBeConstantInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MayBeConstantInspection.kt index 6c39eceb8ed..932c9690a19 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MayBeConstantInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MayBeConstantInspection.kt @@ -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) + ) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MigrateDiagnosticSuppressionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MigrateDiagnosticSuppressionInspection.kt index 7827e61a324..bae06179f56 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MigrateDiagnosticSuppressionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MigrateDiagnosticSuppressionInspection.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt index b47f993adb9..182cf94db7e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt @@ -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( diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt index 37fd18f30e7..ca9ca4f9899 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt @@ -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 #ref $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 #ref $message #loc", + RenamePackageFix() + ) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NullChecksToSafeCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NullChecksToSafeCallInspection.kt index bcac51efc65..83f69012591 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/NullChecksToSafeCallInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NullChecksToSafeCallInspection.kt @@ -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()) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NullableBooleanElvisInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NullableBooleanElvisInspection.kt index 754705f01e3..e1157df8765 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/NullableBooleanElvisInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NullableBooleanElvisInspection.kt @@ -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" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ProtectedInFinalInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ProtectedInFinalInspection.kt index f0a3f184add..e660b3e2e47 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ProtectedInFinalInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ProtectedInFinalInspection.kt @@ -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(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(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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RecursivePropertyAccessorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RecursivePropertyAccessorInspection.kt index 1515f089c20..662f9832f6f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RecursivePropertyAccessorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RecursivePropertyAccessorInspection.kt @@ -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) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantExplicitTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantExplicitTypeInspection.kt index cd5a96444d9..583273a1db6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantExplicitTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantExplicitTypeInspection.kt @@ -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) + ) + }) } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantGetterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantGetterInspection.kt index cc8c585d091..1a88ff40ba5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantGetterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantGetterInspection.kt @@ -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()) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt index 60b41efb0c2..d77bf07fa26 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantIfInspection.kt @@ -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)) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt index f4d46d0458f..534e374b3fe 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt @@ -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()) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantModalityModifierInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantModalityModifierInspection.kt index 6e9cd92dbce..cb8471f4b9c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantModalityModifierInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantModalityModifierInspection.kt @@ -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)) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt index 5ba69e823cf..ba93255062b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt @@ -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): 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): 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() diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSetterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSetterInspection.kt index f166a69d9f1..f06c4553d7e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSetterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSetterInspection.kt @@ -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()) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSuspendModifierInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSuspendModifierInspection.kt index 53835dd5a20..86f8d84cab5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSuspendModifierInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSuspendModifierInspection.kt @@ -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 { 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 { it.hasSuspendCalls(context) }) { + return } - } + + holder.registerProblem(suspendModifier, + "Redundant 'suspend' modifier", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + IntentionWrapper(RemoveModifierFix(function, KtTokens.SUSPEND_KEYWORD, isRedundant = true), + function.containingFile)) + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt index b7f2d3e8c5a..fcff983eed2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt @@ -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(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(strict = true) != null) return + + holder.registerProblem(expression, + "Redundant 'Unit'", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + RemoveRedundantUnitFix()) + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitReturnTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitReturnTypeInspection.kt index 382d7de2a16..6f702872df0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitReturnTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitReturnTypeInspection.kt @@ -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)) } - } + }) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt index 07e6c199426..fac5275c6a8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt @@ -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)) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveRedundantSpreadOperatorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveRedundantSpreadOperatorInspection.kt index fb100ab0f66..feb02b399b7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveRedundantSpreadOperatorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveRedundantSpreadOperatorInspection.kt @@ -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) + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveSetterParameterTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveSetterParameterTypeInspection.kt index 9f381a4e687..e90c0d5931d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveSetterParameterTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveSetterParameterTypeInspection.kt @@ -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)) } } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveToStringInStringTemplateInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveToStringInStringTemplateInspection.kt index 7fddd08dcfb..2f933675974 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveToStringInStringTemplateInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RemoveToStringInStringTemplateInspection.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt index 0d0c55564ee..be29e0474a0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToWithInfixFormInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToWithInfixFormInspection.kt index a76049e8225..38728129520 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToWithInfixFormInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToWithInfixFormInspection.kt @@ -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() + ) + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/SelfAssignmentInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/SelfAssignmentInspection.kt index 6d7d680a92a..02a6abed68a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/SelfAssignmentInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/SelfAssignmentInspection.kt @@ -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, - 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, + 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 } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/SimplifyWhenWithBooleanConstantConditionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/SimplifyWhenWithBooleanConstantConditionInspection.kt index 2c6e361bf06..221d815bb25 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/SimplifyWhenWithBooleanConstantConditionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/SimplifyWhenWithBooleanConstantConditionInspection.kt @@ -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()) + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt index 8abedda09ac..eeb5781cac5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousEqualsCombination.kt @@ -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) { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnsafeCastFromDynamicInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnsafeCastFromDynamicInspection.kt index 76f84c657f1..e2211bbd44b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnsafeCastFromDynamicInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnsafeCastFromDynamicInspection.kt @@ -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") } - } + }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedLambdaExpressionBodyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedLambdaExpressionBodyInspection.kt index 47adce7173e..ff3116d26f7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedLambdaExpressionBodyInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedLambdaExpressionBodyInspection.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index d420d7113a4..d3b905bd770 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -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" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UseExpressionBodyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UseExpressionBodyInspection.kt index b3658d389ca..81c62fe0e54 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UseExpressionBodyInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UseExpressionBodyInspection.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/WhenWithOnlyElseInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/WhenWithOnlyElseInspection.kt index c5c5997337a..a0ec97735b9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/WhenWithOnlyElseInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/WhenWithOnlyElseInspection.kt @@ -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) + ) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt index b4339ef1fc4..e8185ace15b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt @@ -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 ()" diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt index 9b41d22e0da..8d73ac037e8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt @@ -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() == 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() == 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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/kdoc/KDocMissingDocumentationInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/kdoc/KDocMissingDocumentationInspection.kt index 32e12861c4e..c34997c4f44 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/kdoc/KDocMissingDocumentationInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/kdoc/KDocMissingDocumentationInspection.kt @@ -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"