[FIR] Add lightTree support to extended checkers + minor refactorings

This commit is contained in:
vldf
2020-08-19 18:19:21 +03:00
committed by Mikhail Glukhikh
parent da702992d9
commit 36f2f1fcf7
15 changed files with 617 additions and 114 deletions
@@ -1,10 +1,9 @@
fun f() {
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> <!CAN_BE_VAL!>var<!> baz = 0
class LocalClass {
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
}
LocalClass().foo = 1
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> <!CAN_BE_VAL!>var<!> baz = 0
}
internal inline fun internal() {
@@ -1,5 +1,6 @@
FILE: RedundantVisibilityModifierChecker.kt
public final fun f(): R|kotlin/Unit| {
lvar baz: R|kotlin/Int| = Int(0)
local final class LocalClass : R|kotlin/Any| {
public constructor(): R|LocalClass| {
super<R|kotlin/Any|>()
@@ -12,7 +13,6 @@ FILE: RedundantVisibilityModifierChecker.kt
}
R|/LocalClass.LocalClass|().R|/LocalClass.foo| = Int(1)
lvar baz: R|kotlin/Int| = Int(0)
}
internal final inline fun internal(): R|kotlin/Unit| {
R|/f|()
@@ -0,0 +1,314 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.fir;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class ExtendedFirWithLightTreeDiagnosticsTestGenerated extends AbstractExtendedFirWithLightTreeDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInExtendedCheckers() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("ArrayEqualityCanBeReplacedWithEquals.kt")
public void testArrayEqualityCanBeReplacedWithEquals() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/ArrayEqualityCanBeReplacedWithEquals.kt");
}
@TestMetadata("RedundantExplicitTypeChecker.kt")
public void testRedundantExplicitTypeChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt");
}
@TestMetadata("RedundantModalityModifierChecker.kt")
public void testRedundantModalityModifierChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantModalityModifierChecker.kt");
}
@TestMetadata("RedundantReturnUnitTypeChecker.kt")
public void testRedundantReturnUnitTypeChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantReturnUnitTypeChecker.kt");
}
@TestMetadata("RedundantSetterParameterTypeChecker.kt")
public void testRedundantSetterParameterTypeChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSetterParameterTypeChecker.kt");
}
@TestMetadata("RedundantSingleExpressionStringTemplateChecker.kt")
public void testRedundantSingleExpressionStringTemplateChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.kt");
}
@TestMetadata("RedundantVisibilityModifierChecker.kt")
public void testRedundantVisibilityModifierChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantVisibilityModifierChecker.kt");
}
@TestMetadata("VariableAssignmentChecker.kt")
public void testVariableAssignmentChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/VariableAssignmentChecker.kt");
}
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CanBeReplacedWithOperatorAssignment extends AbstractExtendedFirWithLightTreeDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInCanBeReplacedWithOperatorAssignment() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("BasicTest.kt")
public void testBasicTest() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.kt");
}
@TestMetadata("ComplexExpression.kt")
public void testComplexExpression() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.kt");
}
@TestMetadata("flexibleTypeBug.kt")
public void testFlexibleTypeBug() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.kt");
}
@TestMetadata("illegalMultipleOperators.kt")
public void testIllegalMultipleOperators() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.kt");
}
@TestMetadata("illegalMultipleOperatorsMiddle.kt")
public void testIllegalMultipleOperatorsMiddle() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt");
}
@TestMetadata("invalidSubtraction.kt")
public void testInvalidSubtraction() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.kt");
}
@TestMetadata("list.kt")
public void testList() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.kt");
}
@TestMetadata("logicOperators.kt")
public void testLogicOperators() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/logicOperators.kt");
}
@TestMetadata("multipleOperators.kt")
public void testMultipleOperators() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.kt");
}
@TestMetadata("multipleOperatorsRightSideRepeat.kt")
public void testMultipleOperatorsRightSideRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt");
}
@TestMetadata("mutableList.kt")
public void testMutableList() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.kt");
}
@TestMetadata("nonCommutativeRepeat.kt")
public void testNonCommutativeRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.kt");
}
@TestMetadata("nonRepeatingAssignment.kt")
public void testNonRepeatingAssignment() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.kt");
}
@TestMetadata("OperatorAssignment.kt")
public void testOperatorAssignment() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.kt");
}
@TestMetadata("plusAssignConflict.kt")
public void testPlusAssignConflict() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.kt");
}
@TestMetadata("rightSideRepeat.kt")
public void testRightSideRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.kt");
}
@TestMetadata("simpleAssign.kt")
public void testSimpleAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.kt");
}
@TestMetadata("validAddition.kt")
public void testValidAddition() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.kt");
}
@TestMetadata("validSubtraction.kt")
public void testValidSubtraction() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt");
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class EmptyRangeChecker extends AbstractExtendedFirWithLightTreeDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInEmptyRangeChecker() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("NoWarning.kt")
public void testNoWarning() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.kt");
}
@TestMetadata("Warning.kt")
public void testWarning() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.kt");
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantCallOfConversionMethod extends AbstractExtendedFirWithLightTreeDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInRedundantCallOfConversionMethod() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("booleanToInt.kt")
public void testBooleanToInt() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/booleanToInt.kt");
}
@TestMetadata("byte.kt")
public void testByte() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/byte.kt");
}
@TestMetadata("char.kt")
public void testChar() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/char.kt");
}
@TestMetadata("double.kt")
public void testDouble() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/double.kt");
}
@TestMetadata("float.kt")
public void testFloat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/float.kt");
}
@TestMetadata("int.kt")
public void testInt() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/int.kt");
}
@TestMetadata("long.kt")
public void testLong() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/long.kt");
}
@TestMetadata("nullable.kt")
public void testNullable() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/nullable.kt");
}
@TestMetadata("nullable2.kt")
public void testNullable2() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/nullable2.kt");
}
@TestMetadata("safeString.kt")
public void testSafeString() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/safeString.kt");
}
@TestMetadata("safeString2.kt")
public void testSafeString2() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/safeString2.kt");
}
@TestMetadata("short.kt")
public void testShort() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/short.kt");
}
@TestMetadata("string.kt")
public void testString() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/string.kt");
}
@TestMetadata("StringTemplate.kt")
public void testStringTemplate() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/StringTemplate.kt");
}
@TestMetadata("toOtherType.kt")
public void testToOtherType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/toOtherType.kt");
}
@TestMetadata("uByte.kt")
public void testUByte() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/uByte.kt");
}
@TestMetadata("uInt.kt")
public void testUInt() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/uInt.kt");
}
@TestMetadata("uLong.kt")
public void testULong() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/uLong.kt");
}
@TestMetadata("uShort.kt")
public void testUShort() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/uShort.kt");
}
@TestMetadata("variable.kt")
public void testVariable() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantCallOfConversionMethod/variable.kt");
}
}
}
@@ -5,12 +5,11 @@
package org.jetbrains.kotlin.fir.analysis.checkers
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.fir.Visibilities
import org.jetbrains.kotlin.fir.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
@@ -223,15 +222,18 @@ fun FirSimpleFunction.overriddenFunctions(
/**
* Returns the visibility by given KtModifierList
*/
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toFirVisibility()
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toVisibilityOrNull()
fun KtModifierKeywordToken.toFirVisibility(): Visibility {
/**
* Returns Visibility by token or null
*/
fun KtModifierKeywordToken.toVisibilityOrNull(): Visibility? {
return when (this) {
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
KtTokens.INTERNAL_KEYWORD -> Visibilities.Internal
else -> throw IllegalArgumentException("Unknown visibility modifier:$this")
else -> null
}
}
@@ -256,10 +258,10 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
val klass = context.findClosestClassOrObject() ?: return Modality.FINAL
val modifiers = this.modifierListOrNull() ?: return Modality.FINAL
if (modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
if (modifiers.contains(KtTokens.OVERRIDE_KEYWORD)) {
val klassModifiers = klass.modifierListOrNull()
if (klassModifiers != null && klassModifiers.run {
hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.SEALED_KEYWORD)
contains(KtTokens.ABSTRACT_KEYWORD) || contains(KtTokens.OPEN_KEYWORD) || contains(KtTokens.SEALED_KEYWORD)
}) {
return Modality.OPEN
}
@@ -268,7 +270,7 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
if (
klass is FirRegularClass
&& klass.classKind == ClassKind.INTERFACE
&& !modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD)
&& !modifiers.contains(KtTokens.PRIVATE_KEYWORD)
) {
return if (this.hasBody()) Modality.OPEN else Modality.ABSTRACT
}
@@ -276,7 +278,7 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
return Modality.FINAL
}
private fun FirDeclaration.modifierListOrNull() = (this.source.getModifierList() as? FirPsiModifierList)?.modifierList
private fun FirDeclaration.modifierListOrNull() = this.source.getModifierList()?.modifiers?.map { it.token }
private fun FirDeclaration.hasBody(): Boolean = when (this) {
is FirSimpleFunction -> this.body != null && this.body !is FirEmptyExpressionBlock
@@ -306,3 +308,23 @@ fun FirClass<*>.findNonInterfaceSupertype(context: CheckerContext): FirTypeRef?
return null
}
/**
* Returns the source element of the eq operator in assignment statement
*/
fun FirSourceElement.eqOperatorSource(): FirSourceElement? {
return when (this) {
is FirLightSourceElement -> {
val children = Ref<Array<LighterASTNode>>()
val tree = tree
tree.getChildren(element, children)
val element = children.get().getOrNull(2) ?: return null
element.toFirLightSourceElement(element.startOffset, element.endOffset, tree)
}
is FirPsiSourceElement<*> -> {
val operator = psi.children.getOrNull(1)
operator?.toFirPsiSourceElement()
}
else -> null
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.eqOperatorSource
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS
@@ -13,9 +14,7 @@ import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
@@ -29,6 +28,6 @@ object ArrayEqualityCanBeReplacedWithEquals : FirBasicExpresionChecker() {
if (left.typeRef.coneType.classId != StandardClassIds.Array) return
if (right.typeRef.coneType.classId != StandardClassIds.Array) return
reporter.report(expression.psi?.children?.get(1)?.toFirPsiSourceElement(), ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS)
reporter.report(expression.source?.eqOperatorSource(), ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS)
}
}
@@ -5,9 +5,16 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirVariableAssignmentChecker
import org.jetbrains.kotlin.fir.analysis.checkers.eqOperatorSource
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirExpressionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
@@ -16,39 +23,85 @@ import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtOperationReferenceExpression
object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker() {
object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker<FirVariableAssignment>() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val lValue = expression.lValue
if (lValue !is FirResolvedNamedReference) return
if (expression.source?.kind is FirFakeSourceElementKind) return
val operator = expression.psi?.children?.getOrNull(1) ?: return
val operationSign = (operator as? KtOperationReferenceExpression)?.operationSignTokenType
if (operationSign != KtTokens.EQ) return
val lValuePsi = lValue.psi as? KtNameReferenceExpression ?: return
val rValue = expression.rValue as? FirFunctionCall ?: return
val rValuePsi = rValue.psi as? KtBinaryExpression ?: return
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
if (rValue.source?.kind is FirFakeSourceElementKind) return
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
if (rValueClassId !in StandardClassIds.primitiveTypes) return
val rValueResolvedSymbol = rValue.toResolvedCallableSymbol() ?: return
if (rValueResolvedSymbol.callableId.classId !in StandardClassIds.primitiveTypes) return
if (rValuePsi.matcher(lValuePsi)) {
val operatorStatement = operator.toFirPsiSourceElement()
reporter.report(operatorStatement, FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT)
var needToReport = false
val assignmentSource = expression.source
if (assignmentSource is FirPsiSourceElement<*>) {
val lValuePsi = lValue.psi as? KtNameReferenceExpression ?: return
val rValuePsi = rValue.psi as? KtBinaryExpression ?: return
if (rValuePsi.matcher(lValuePsi)) {
needToReport = true
}
} else if (assignmentSource is FirLightSourceElement) {
val lValueLightTree = (lValue.source as FirLightSourceElement).element
val rValueLightTree = (rValue.source as FirLightSourceElement).element
if (lightTreeMatcher(lValueLightTree, rValueLightTree, assignmentSource)) {
needToReport = true
}
}
if (needToReport) {
val reportSource = expression.source?.eqOperatorSource()
reporter.report(reportSource, FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT)
}
}
fun KtBinaryExpression.matcher(variable: KtNameReferenceExpression): Boolean {
fun lightTreeMatcher(
variable: LighterASTNode,
expression: LighterASTNode,
source: FirLightSourceElement,
prevOperator: LighterASTNode? = null
): Boolean {
val tree = source.tree
val childrenNullable = Ref<Array<LighterASTNode?>>()
tree.getChildren(expression, childrenNullable)
val children = childrenNullable.get().filterNotNull()
val operator = children.firstOrNull { it.tokenType == KtNodeTypes.OPERATION_REFERENCE }
if (prevOperator != null && !isLightNodesHierarchicallyTrue(prevOperator, operator)) return false
if (operator?.canBeAugmented() == false) return false
val commutative = operator != null && isCommutativeOperator(operator)
var afterOperatorNode = false
children.forEach {
when (it.tokenType) {
KtNodeTypes.REFERENCE_EXPRESSION -> {
if ((commutative || !afterOperatorNode) && it.toString() == variable.toString()) return true
}
KtNodeTypes.BINARY_EXPRESSION -> {
return lightTreeMatcher(variable, it, source, operator)
}
KtNodeTypes.OPERATION_REFERENCE -> {
afterOperatorNode = true
}
}
}
return false
}
private fun KtBinaryExpression.matcher(variable: KtNameReferenceExpression): Boolean {
if (!canBeAugmented()) return false
if ((left as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName()) return true
if ((right as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName() && isCommutative()) return true
@@ -60,9 +113,11 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker
val isLeftMatch = isHierarchicallyTrue(operationToken, leftExpression?.operationToken)
&& leftExpression?.matcher(variable) == true
if (isLeftMatch) return true
val isRightMatch = isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
&& rightExpression?.matcher(variable) == true
if (isRightMatch) return true
return (isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
&& rightExpression?.matcher(variable) == true)
false
} else {
val leftExpression = left as? KtBinaryExpression
@@ -80,4 +135,13 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker
|| this.operationToken == KtTokens.PERC
private fun isHierarchicallyTrue(currentOperation: IElementType, nextOperation: IElementType?) = currentOperation == nextOperation
private fun isCommutativeOperator(operator: LighterASTNode) = operator.toString().let { it == "+" || it == "*" }
private fun isLightNodesHierarchicallyTrue(first: LighterASTNode?, second: LighterASTNode?) =
first.toString() == second.toString()
private fun LighterASTNode.canBeAugmented() = this.toString().let {
it == "+" || it == "*" || it == "-" || it == "/" || it == "%"
}
}
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
import org.jetbrains.kotlin.types.expressions.OperatorConventions
object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -37,6 +35,21 @@ object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
}
}
private fun FirExpression.isRedundant(qualifiedClassId: ClassId): Boolean {
val thisType = if (this is FirConstExpression<*>) {
this.typeRef.coneType.classId
} else {
when {
typeRef.coneType is ConeFlexibleType -> null
psi?.parent !is KtSafeQualifiedExpression
&& (psi is KtSafeQualifiedExpression || typeRef.coneType.isMarkedNullable) -> null
this.typeRef.coneType.isMarkedNullable -> null
else -> this.typeRef.coneType.classId
}
}
return thisType == qualifiedClassId
}
private val targetClassMap = hashMapOf(
"toString" to StandardClassIds.String,
"toDouble" to StandardClassIds.Double,
@@ -51,26 +64,4 @@ object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
"toUShort" to StandardClassIds.UShort,
"toUByte" to StandardClassIds.UByte
)
private fun FirExpression.isRedundant(qualifiedClassId: ClassId): Boolean {
val thisType = if (this is FirConstExpression<*>) {
this.typeRef.coneType.classId
} else {
val binaryExpression = psi as? KtBinaryExpression
val operator = binaryExpression?.operationToken
if (operator in OperatorConventions.COMPARISON_OPERATIONS) {
// Special case here because compareTo returns Int
StandardClassIds.Boolean
} else {
when {
typeRef.coneType is ConeFlexibleType -> null
psi?.parent !is KtSafeQualifiedExpression
&& (psi is KtSafeQualifiedExpression || typeRef.coneType.isMarkedNullable) -> null
this.typeRef.coneType.isMarkedNullable -> null
else -> this.typeRef.coneType.classId
}
}
}
return thisType == qualifiedClassId
}
}
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
@@ -32,7 +34,7 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
val initializer = declaration.initializer ?: return
val typeReference = declaration.returnTypeRef
if (typeReference.source is FirFakeSourceElement<*>) return
if (typeReference.source?.kind is FirFakeSourceElementKind) return
val type = declaration.returnTypeRef.coneType
@@ -46,14 +48,14 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
if (!type.isSame(StandardClassIds.Boolean)) return
}
KtNodeTypes.INTEGER_CONSTANT -> {
if (initializer.text?.endsWith("L") == true) {
if (initializer.kind == FirConstKind.Long) {
if (!type.isSame(StandardClassIds.Long)) return
} else {
if (!type.isSame(StandardClassIds.Int)) return
}
}
KtNodeTypes.FLOAT_CONSTANT -> {
if (initializer.text?.endsWith("f", ignoreCase = true) == true) {
if (initializer.kind == FirConstKind.Float) {
if (!type.isSame(StandardClassIds.Float)) return
} else {
if (!type.isSame(StandardClassIds.Double)) return
@@ -89,11 +91,18 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
reporter.report(declaration.returnTypeRef.source, FirErrors.REDUNDANT_EXPLICIT_TYPE)
}
private val FirExpression.text
get() = this.source.psi?.text
private val FirTypeRef.text
get() = this.psi?.text
private val FirTypeRef.text: String?
get() {
return when (source) {
is FirPsiSourceElement<*> -> {
source.psi?.text
}
is FirLightSourceElement -> {
(source as FirLightSourceElement).element.toString()
}
else -> null
}
}
private fun ConeKotlinType.isSame(other: ClassId?): Boolean {
if (this.nullability.isNullable) return false
@@ -7,23 +7,24 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.FirModifier
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList
import org.jetbrains.kotlin.fir.analysis.checkers.implicitModality
import org.jetbrains.kotlin.fir.analysis.checkers.source
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.modality
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.lexer.KtTokens
object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.source is FirFakeSourceElement<*>) return
if (declaration.source?.kind is FirFakeSourceElementKind) return
val modality = declaration.modality ?: return
if (
@@ -35,7 +36,16 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
if (modality != implicitModality) return
val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return
reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER)
val modalityModifierSource = declaration.source.getModifierList()?.modifiers?.modalitySource()
reporter.report(modalityModifierSource, REDUNDANT_MODALITY_MODIFIER)
}
private fun Collection<FirModifier<*>>.modalitySource(): FirSourceElement? {
return this.firstOrNull {
it.token == KtTokens.FINAL_KEYWORD
|| it.token == KtTokens.OPEN_KEYWORD
|| it.token == KtTokens.SEALED_KEYWORD
|| it.token == KtTokens.ABSTRACT_KEYWORD
}?.source
}
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
@@ -21,7 +21,7 @@ object RedundantReturnUnitType : FirBasicDeclarationChecker() {
if (declaration.body is FirSingleExpressionBlock) return
val returnType = declaration.returnTypeRef
if (returnType.source == null) return
if (declaration.source is FirFakeSourceElement<*>) return
if (declaration.source?.kind is FirFakeSourceElementKind) return
if (returnType.annotations.isNotEmpty()) return
if (returnType.isUnit) {
@@ -5,8 +5,14 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import com.intellij.lang.LighterASTNode
import com.intellij.lang.PsiBuilder
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
@@ -20,19 +26,42 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker() {
override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (functionCall.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return
if (functionCall !is FirFunctionCall) return
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return
if (expression !is FirFunctionCall) return
if (
functionCall.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
&& functionCall.psi?.findStringParent()?.children?.size == 1 // there is no more children in original string template
expression.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
&& expression.stringParentChildrenCount() == 1 // there is no more children in original string template
) {
reporter.report(functionCall.source, REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE)
reporter.report(expression.source, REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE)
}
}
private fun PsiElement.findStringParent(): KtStringTemplateExpression? {
if (this is KtStringTemplateExpression) return this
return this.parent?.findStringParent()
private fun FirStatement.stringParentChildrenCount(): Int? {
return when (source) {
is FirPsiSourceElement<*> -> {
source.psi?.stringParentChildrenCount()
}
is FirLightSourceElement -> {
(source as FirLightSourceElement).element.stringParentChildrenCount(source as FirLightSourceElement)
}
else -> null
}
}
}
private fun PsiElement.stringParentChildrenCount(): Int? {
if (parent is KtStringTemplateExpression) return parent?.children?.size
return parent.stringParentChildrenCount()
}
private fun LighterASTNode.stringParentChildrenCount(source: FirLightSourceElement): Int? {
val parent = source.tree.getParent(this)
return if (parent?.tokenType == KtNodeTypes.STRING_TEMPLATE) {
val childrenOfParent = Ref<Array<LighterASTNode>>()
source.tree.getChildren(parent!!, childrenOfParent)
childrenOfParent.get().filter { it is PsiBuilder.Marker }.size
} else {
parent?.stringParentChildrenCount(source)
}
}
}
@@ -7,54 +7,54 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.Visibilities
import org.jetbrains.kotlin.fir.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirConstructor && declaration.source is FirFakeSourceElement<*>) return
if (declaration is FirConstructor && declaration.source?.kind is FirFakeSourceElementKind) return
if (declaration.source is FirFakeSourceElement<*>) return
if (
declaration !is FirMemberDeclaration
&& !(declaration is FirPropertyAccessor && declaration.visibility == context.containingPropertyVisibility)
) return
val modifierList = (declaration.source.getModifierList() as? FirPsiModifierList)?.modifierList ?: return
val visibilityModifier = modifierList.getVisibility() ?: return
val modifiers = declaration.source.getModifierList()
val visibilityModifier = when (modifiers) {
is FirPsiModifierList -> modifiers.modifierList.getVisibility()
is FirLightModifierList -> modifiers.modifiers.visibilityOrNull()
else -> null
} ?: return
val implicitVisibility = declaration.implicitVisibility(context)
val containingMemberDeclaration = context.findClosest<FirMemberDeclaration>()
val redundantVisibility = when {
visibilityModifier == implicitVisibility -> implicitVisibility
modifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) &&
containingMemberDeclaration.let {
it != null && (it.isLocalMember || modifierList.hasModifier(KtTokens.PRIVATE_KEYWORD))
modifiers?.modifiers.hasModifier(KtTokens.INTERNAL_KEYWORD) &&
containingMemberDeclaration.let { decl ->
decl != null && (decl.isLocalMember || modifiers?.modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD))
} -> Visibilities.Internal
else -> null
} ?: return
else -> return
}
if (
redundantVisibility == Visibilities.Public
&& declaration is FirProperty
&& modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& modifiers?.modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& declaration.isVar
&& declaration.setter?.visibility == Visibilities.Public
) return
val source = modifierList.visibilityModifier()?.toFirPsiSourceElement()
reporter.report(source, FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
reporter.report(declaration.source.modifierSource, FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
}
private fun FirDeclaration.implicitVisibility(context: CheckerContext): Visibility {
@@ -124,4 +124,23 @@ object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
private val CheckerContext.containingPropertyVisibility
get() = (this.containingDeclarations.last() as? FirProperty)?.visibility
private val FirSourceElement?.modifierSource: FirSourceElement?
get() = when (this) {
null -> null
is FirPsiSourceElement<*> -> (psi as? KtModifierListOwner)?.modifierList?.visibilityModifier()?.toFirPsiSourceElement()
is FirLightSourceElement -> {
val modifier = this.getModifierList()?.modifiers?.firstOrNull { it.isVisibilityModifier }
modifier?.source
}
}
private val FirModifier<*>.isVisibilityModifier
get() = this.token.toVisibilityOrNull() != null
private fun List<FirLightModifier>.visibilityOrNull() =
firstOrNull { it.token.toVisibilityOrNull() != null }?.token?.toVisibilityOrNull()
private fun List<FirModifier<*>>?.hasModifier(token: KtModifierKeywordToken) = this != null && any { it.token == token }
}
@@ -6,20 +6,20 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo
import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection
import org.jetbrains.kotlin.fir.analysis.cfa.traverse
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtProperty
@@ -50,8 +50,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
val source = symbol.getValOrVarSource
if (symbol.callableId.callableName.asString() == "<destruct>") {
lastDestructuringSource = symbol.getValOrVarSource
val childrenCount = symbol.fir.psi?.children?.size ?: continue
lastDestructuredVariables = childrenCount - 1 // -1 cuz we don't need expression node after equals operator
lastDestructuredVariables = symbol.getDestructuringChildrenCount() ?: continue
destructuringCanBeVal = true
continue
}
@@ -65,7 +64,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
destructuringCanBeVal = false
}
lastDestructuredVariables--
} else if (canBeVal(symbol, value) && symbol.fir.delegate == null ) {
} else if (canBeVal(symbol, value) && symbol.fir.delegate == null) {
reporter.report(source, FirErrors.CAN_BE_VAL)
}
}
@@ -106,7 +105,32 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
}
private val FirPropertySymbol.getValOrVarSource
get() = (fir.psi as? KtProperty)?.valOrVarKeyword?.toFirPsiSourceElement()
?: fir.psi?.firstChild?.toFirPsiSourceElement()
?: fir.source
get() = when (fir.source) {
is FirSourceElement -> {
(fir.psi as? KtProperty)?.valOrVarKeyword?.toFirPsiSourceElement()
?: fir.psi?.firstChild?.toFirPsiSourceElement()
?: fir.source
}
is FirLightSourceElement -> {
val children = Ref<Array<LighterASTNode>>()
val tree = (fir.source as FirLightSourceElement).tree
tree.getChildren(tree.root, children)
children.get().first { it.tokenType == KtTokens.VAL_KEYWORD || it.tokenType == KtTokens.VAR_KEYWORD }.let {
it.toFirLightSourceElement(it.startOffset, it.endOffset, tree)
}
}
else -> null
}
private fun FirPropertySymbol.getDestructuringChildrenCount(): Int? = when (fir.source) {
is FirPsiSourceElement<*> -> fir.psi?.children?.size?.minus(1) // -1 cuz we don't need expression node after equals operator
is FirLightSourceElement -> {
val source = fir.source as FirLightSourceElement
val tree = (fir.source as FirLightSourceElement).tree
val children = Ref<Array<LighterASTNode?>>()
tree.getChildren(source.element, children)
children.get().count { it != null }
}
else -> null
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.fir
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.ExtendedDeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExtendedExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkersComponent
abstract class AbstractExtendedFirWithLightTreeDiagnosticsTest : AbstractFirDiagnosticsWithLightTreeTest() {
override fun configureSession(session: FirSession) {
session.checkersComponent.register(ExtendedDeclarationCheckers)
session.checkersComponent.register(ExtendedExpressionCheckers)
}
}
@@ -646,5 +646,11 @@ fun main(args: Array<String>) {
model("extendedCheckers", pattern = KT_WITHOUT_DOTS_IN_NAME)
}
}
testGroup("compiler/fir/analysis-tests/tests", "compiler/fir/analysis-tests/testData") {
testClass<AbstractExtendedFirWithLightTreeDiagnosticsTest> {
model("extendedCheckers", pattern = KT_WITHOUT_DOTS_IN_NAME)
}
}
}
}