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