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