[FIR] Add lightTree support to extended checkers + minor refactorings
This commit is contained in:
Vendored
+1
-2
@@ -1,10 +1,9 @@
|
|||||||
fun f() {
|
fun f() {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> <!CAN_BE_VAL!>var<!> baz = 0
|
||||||
class LocalClass {
|
class LocalClass {
|
||||||
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
|
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
|
||||||
}
|
}
|
||||||
LocalClass().foo = 1
|
LocalClass().foo = 1
|
||||||
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> <!CAN_BE_VAL!>var<!> baz = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun internal() {
|
internal inline fun internal() {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,6 @@
|
|||||||
FILE: RedundantVisibilityModifierChecker.kt
|
FILE: RedundantVisibilityModifierChecker.kt
|
||||||
public final fun f(): R|kotlin/Unit| {
|
public final fun f(): R|kotlin/Unit| {
|
||||||
|
lvar baz: R|kotlin/Int| = Int(0)
|
||||||
local final class LocalClass : R|kotlin/Any| {
|
local final class LocalClass : R|kotlin/Any| {
|
||||||
public constructor(): R|LocalClass| {
|
public constructor(): R|LocalClass| {
|
||||||
super<R|kotlin/Any|>()
|
super<R|kotlin/Any|>()
|
||||||
@@ -12,7 +13,6 @@ FILE: RedundantVisibilityModifierChecker.kt
|
|||||||
}
|
}
|
||||||
|
|
||||||
R|/LocalClass.LocalClass|().R|/LocalClass.foo| = Int(1)
|
R|/LocalClass.LocalClass|().R|/LocalClass.foo| = Int(1)
|
||||||
lvar baz: R|kotlin/Int| = Int(0)
|
|
||||||
}
|
}
|
||||||
internal final inline fun internal(): R|kotlin/Unit| {
|
internal final inline fun internal(): R|kotlin/Unit| {
|
||||||
R|/f|()
|
R|/f|()
|
||||||
|
|||||||
+314
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
-11
@@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
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.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
|
||||||
import org.jetbrains.kotlin.fir.Visibilities
|
|
||||||
import org.jetbrains.kotlin.fir.Visibility
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||||
@@ -223,15 +222,18 @@ fun FirSimpleFunction.overriddenFunctions(
|
|||||||
/**
|
/**
|
||||||
* Returns the visibility by given KtModifierList
|
* 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) {
|
return when (this) {
|
||||||
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
|
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
|
||||||
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
|
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
|
||||||
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
|
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
|
||||||
KtTokens.INTERNAL_KEYWORD -> Visibilities.Internal
|
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 klass = context.findClosestClassOrObject() ?: return Modality.FINAL
|
||||||
val modifiers = this.modifierListOrNull() ?: 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()
|
val klassModifiers = klass.modifierListOrNull()
|
||||||
if (klassModifiers != null && klassModifiers.run {
|
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
|
return Modality.OPEN
|
||||||
}
|
}
|
||||||
@@ -268,7 +270,7 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
|
|||||||
if (
|
if (
|
||||||
klass is FirRegularClass
|
klass is FirRegularClass
|
||||||
&& klass.classKind == ClassKind.INTERFACE
|
&& klass.classKind == ClassKind.INTERFACE
|
||||||
&& !modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD)
|
&& !modifiers.contains(KtTokens.PRIVATE_KEYWORD)
|
||||||
) {
|
) {
|
||||||
return if (this.hasBody()) Modality.OPEN else Modality.ABSTRACT
|
return if (this.hasBody()) Modality.OPEN else Modality.ABSTRACT
|
||||||
}
|
}
|
||||||
@@ -276,7 +278,7 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
|
|||||||
return Modality.FINAL
|
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) {
|
private fun FirDeclaration.hasBody(): Boolean = when (this) {
|
||||||
is FirSimpleFunction -> this.body != null && this.body !is FirEmptyExpressionBlock
|
is FirSimpleFunction -> this.body != null && this.body !is FirEmptyExpressionBlock
|
||||||
@@ -306,3 +308,23 @@ fun FirClass<*>.findNonInterfaceSupertype(context: CheckerContext): FirTypeRef?
|
|||||||
|
|
||||||
return null
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-3
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
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.checkers.expression.FirBasicExpresionChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS
|
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.FirOperation
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
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.symbols.StandardClassIds
|
||||||
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
|
|
||||||
import org.jetbrains.kotlin.fir.types.classId
|
import org.jetbrains.kotlin.fir.types.classId
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
|
|
||||||
@@ -29,6 +28,6 @@ object ArrayEqualityCanBeReplacedWithEquals : FirBasicExpresionChecker() {
|
|||||||
if (left.typeRef.coneType.classId != StandardClassIds.Array) return
|
if (left.typeRef.coneType.classId != StandardClassIds.Array) return
|
||||||
if (right.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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+81
-17
@@ -5,9 +5,16 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
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 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.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.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
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.psi
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
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.classId
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
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) {
|
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
val lValue = expression.lValue
|
val lValue = expression.lValue
|
||||||
if (lValue !is FirResolvedNamedReference) return
|
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 rValue = expression.rValue as? FirFunctionCall ?: return
|
||||||
val rValuePsi = rValue.psi as? KtBinaryExpression ?: return
|
if (rValue.source?.kind is FirFakeSourceElementKind) return
|
||||||
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
|
|
||||||
|
|
||||||
|
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
|
||||||
if (rValueClassId !in StandardClassIds.primitiveTypes) return
|
if (rValueClassId !in StandardClassIds.primitiveTypes) return
|
||||||
val rValueResolvedSymbol = rValue.toResolvedCallableSymbol() ?: return
|
val rValueResolvedSymbol = rValue.toResolvedCallableSymbol() ?: return
|
||||||
if (rValueResolvedSymbol.callableId.classId !in StandardClassIds.primitiveTypes) return
|
if (rValueResolvedSymbol.callableId.classId !in StandardClassIds.primitiveTypes) return
|
||||||
|
|
||||||
if (rValuePsi.matcher(lValuePsi)) {
|
var needToReport = false
|
||||||
val operatorStatement = operator.toFirPsiSourceElement()
|
val assignmentSource = expression.source
|
||||||
reporter.report(operatorStatement, FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT)
|
|
||||||
|
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 (!canBeAugmented()) return false
|
||||||
if ((left as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName()) return true
|
if ((left as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName()) return true
|
||||||
if ((right as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName() && isCommutative()) 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)
|
val isLeftMatch = isHierarchicallyTrue(operationToken, leftExpression?.operationToken)
|
||||||
&& leftExpression?.matcher(variable) == true
|
&& leftExpression?.matcher(variable) == true
|
||||||
if (isLeftMatch) return true
|
if (isLeftMatch) return true
|
||||||
|
val isRightMatch = isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
|
||||||
|
&& rightExpression?.matcher(variable) == true
|
||||||
|
if (isRightMatch) return true
|
||||||
|
|
||||||
return (isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
|
false
|
||||||
&& rightExpression?.matcher(variable) == true)
|
|
||||||
} else {
|
} else {
|
||||||
val leftExpression = left as? KtBinaryExpression
|
val leftExpression = left as? KtBinaryExpression
|
||||||
|
|
||||||
@@ -80,4 +135,13 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker
|
|||||||
|| this.operationToken == KtTokens.PERC
|
|| this.operationToken == KtTokens.PERC
|
||||||
|
|
||||||
private fun isHierarchicallyTrue(currentOperation: IElementType, nextOperation: IElementType?) = currentOperation == nextOperation
|
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 == "%"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+15
-24
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.fir.types.classId
|
|||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
import org.jetbrains.kotlin.fir.types.isMarkedNullable
|
import org.jetbrains.kotlin.fir.types.isMarkedNullable
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
|
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
|
||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
|
||||||
|
|
||||||
object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
|
object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
|
||||||
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
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(
|
private val targetClassMap = hashMapOf(
|
||||||
"toString" to StandardClassIds.String,
|
"toString" to StandardClassIds.String,
|
||||||
"toDouble" to StandardClassIds.Double,
|
"toDouble" to StandardClassIds.Double,
|
||||||
@@ -51,26 +64,4 @@ object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
|
|||||||
"toUShort" to StandardClassIds.UShort,
|
"toUShort" to StandardClassIds.UShort,
|
||||||
"toUByte" to StandardClassIds.UByte
|
"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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+18
-9
@@ -6,7 +6,9 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
||||||
|
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
@@ -32,7 +34,7 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
|
|||||||
val initializer = declaration.initializer ?: return
|
val initializer = declaration.initializer ?: return
|
||||||
val typeReference = declaration.returnTypeRef
|
val typeReference = declaration.returnTypeRef
|
||||||
|
|
||||||
if (typeReference.source is FirFakeSourceElement<*>) return
|
if (typeReference.source?.kind is FirFakeSourceElementKind) return
|
||||||
|
|
||||||
val type = declaration.returnTypeRef.coneType
|
val type = declaration.returnTypeRef.coneType
|
||||||
|
|
||||||
@@ -46,14 +48,14 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
|
|||||||
if (!type.isSame(StandardClassIds.Boolean)) return
|
if (!type.isSame(StandardClassIds.Boolean)) return
|
||||||
}
|
}
|
||||||
KtNodeTypes.INTEGER_CONSTANT -> {
|
KtNodeTypes.INTEGER_CONSTANT -> {
|
||||||
if (initializer.text?.endsWith("L") == true) {
|
if (initializer.kind == FirConstKind.Long) {
|
||||||
if (!type.isSame(StandardClassIds.Long)) return
|
if (!type.isSame(StandardClassIds.Long)) return
|
||||||
} else {
|
} else {
|
||||||
if (!type.isSame(StandardClassIds.Int)) return
|
if (!type.isSame(StandardClassIds.Int)) return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KtNodeTypes.FLOAT_CONSTANT -> {
|
KtNodeTypes.FLOAT_CONSTANT -> {
|
||||||
if (initializer.text?.endsWith("f", ignoreCase = true) == true) {
|
if (initializer.kind == FirConstKind.Float) {
|
||||||
if (!type.isSame(StandardClassIds.Float)) return
|
if (!type.isSame(StandardClassIds.Float)) return
|
||||||
} else {
|
} else {
|
||||||
if (!type.isSame(StandardClassIds.Double)) return
|
if (!type.isSame(StandardClassIds.Double)) return
|
||||||
@@ -89,11 +91,18 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() {
|
|||||||
reporter.report(declaration.returnTypeRef.source, FirErrors.REDUNDANT_EXPLICIT_TYPE)
|
reporter.report(declaration.returnTypeRef.source, FirErrors.REDUNDANT_EXPLICIT_TYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val FirExpression.text
|
private val FirTypeRef.text: String?
|
||||||
get() = this.source.psi?.text
|
get() {
|
||||||
|
return when (source) {
|
||||||
private val FirTypeRef.text
|
is FirPsiSourceElement<*> -> {
|
||||||
get() = this.psi?.text
|
source.psi?.text
|
||||||
|
}
|
||||||
|
is FirLightSourceElement -> {
|
||||||
|
(source as FirLightSourceElement).element.toString()
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun ConeKotlinType.isSame(other: ClassId?): Boolean {
|
private fun ConeKotlinType.isSame(other: ClassId?): Boolean {
|
||||||
if (this.nullability.isNullable) return false
|
if (this.nullability.isNullable) return false
|
||||||
|
|||||||
+18
-8
@@ -7,23 +7,24 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
|
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.implicitModality
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.source
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.modality
|
import org.jetbrains.kotlin.fir.declarations.modality
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
|
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
|
|
||||||
|
|
||||||
object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
|
object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
|
||||||
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
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
|
val modality = declaration.modality ?: return
|
||||||
if (
|
if (
|
||||||
@@ -35,7 +36,16 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
|
|||||||
|
|
||||||
if (modality != implicitModality) return
|
if (modality != implicitModality) return
|
||||||
|
|
||||||
val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return
|
val modalityModifierSource = declaration.source.getModifierList()?.modifiers?.modalitySource()
|
||||||
reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER)
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
@@ -21,7 +21,7 @@ object RedundantReturnUnitType : FirBasicDeclarationChecker() {
|
|||||||
if (declaration.body is FirSingleExpressionBlock) return
|
if (declaration.body is FirSingleExpressionBlock) return
|
||||||
val returnType = declaration.returnTypeRef
|
val returnType = declaration.returnTypeRef
|
||||||
if (returnType.source == null) return
|
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.annotations.isNotEmpty()) return
|
||||||
|
|
||||||
if (returnType.isUnit) {
|
if (returnType.isUnit) {
|
||||||
|
|||||||
+39
-10
@@ -5,8 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
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 com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
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
|
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
||||||
|
|
||||||
object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker() {
|
object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker() {
|
||||||
override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
if (functionCall.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return
|
if (expression.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return
|
||||||
if (functionCall !is FirFunctionCall) return
|
if (expression !is FirFunctionCall) return
|
||||||
if (
|
if (
|
||||||
functionCall.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
|
expression.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
|
||||||
&& functionCall.psi?.findStringParent()?.children?.size == 1 // there is no more children in original string template
|
&& 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? {
|
private fun FirStatement.stringParentChildrenCount(): Int? {
|
||||||
if (this is KtStringTemplateExpression) return this
|
return when (source) {
|
||||||
return this.parent?.findStringParent()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
-17
@@ -7,54 +7,54 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElement
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.Visibilities
|
|
||||||
import org.jetbrains.kotlin.fir.Visibility
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
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.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
||||||
|
|
||||||
object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
|
object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
|
||||||
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
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.source is FirFakeSourceElement<*>) return
|
||||||
if (
|
if (
|
||||||
declaration !is FirMemberDeclaration
|
declaration !is FirMemberDeclaration
|
||||||
&& !(declaration is FirPropertyAccessor && declaration.visibility == context.containingPropertyVisibility)
|
&& !(declaration is FirPropertyAccessor && declaration.visibility == context.containingPropertyVisibility)
|
||||||
) return
|
) return
|
||||||
|
|
||||||
val modifierList = (declaration.source.getModifierList() as? FirPsiModifierList)?.modifierList ?: return
|
val modifiers = declaration.source.getModifierList()
|
||||||
val visibilityModifier = modifierList.getVisibility() ?: return
|
val visibilityModifier = when (modifiers) {
|
||||||
|
is FirPsiModifierList -> modifiers.modifierList.getVisibility()
|
||||||
|
is FirLightModifierList -> modifiers.modifiers.visibilityOrNull()
|
||||||
|
else -> null
|
||||||
|
} ?: return
|
||||||
val implicitVisibility = declaration.implicitVisibility(context)
|
val implicitVisibility = declaration.implicitVisibility(context)
|
||||||
val containingMemberDeclaration = context.findClosest<FirMemberDeclaration>()
|
val containingMemberDeclaration = context.findClosest<FirMemberDeclaration>()
|
||||||
|
|
||||||
val redundantVisibility = when {
|
val redundantVisibility = when {
|
||||||
visibilityModifier == implicitVisibility -> implicitVisibility
|
visibilityModifier == implicitVisibility -> implicitVisibility
|
||||||
|
modifiers?.modifiers.hasModifier(KtTokens.INTERNAL_KEYWORD) &&
|
||||||
modifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) &&
|
containingMemberDeclaration.let { decl ->
|
||||||
containingMemberDeclaration.let {
|
decl != null && (decl.isLocalMember || modifiers?.modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD))
|
||||||
it != null && (it.isLocalMember || modifierList.hasModifier(KtTokens.PRIVATE_KEYWORD))
|
|
||||||
} -> Visibilities.Internal
|
} -> Visibilities.Internal
|
||||||
|
else -> return
|
||||||
else -> null
|
}
|
||||||
} ?: return
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
redundantVisibility == Visibilities.Public
|
redundantVisibility == Visibilities.Public
|
||||||
&& declaration is FirProperty
|
&& declaration is FirProperty
|
||||||
&& modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
&& modifiers?.modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||||
&& declaration.isVar
|
&& declaration.isVar
|
||||||
&& declaration.setter?.visibility == Visibilities.Public
|
&& declaration.setter?.visibility == Visibilities.Public
|
||||||
) return
|
) return
|
||||||
|
|
||||||
val source = modifierList.visibilityModifier()?.toFirPsiSourceElement()
|
reporter.report(declaration.source.modifierSource, FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
|
||||||
reporter.report(source, FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirDeclaration.implicitVisibility(context: CheckerContext): Visibility {
|
private fun FirDeclaration.implicitVisibility(context: CheckerContext): Visibility {
|
||||||
@@ -124,4 +124,23 @@ object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
|
|||||||
|
|
||||||
private val CheckerContext.containingPropertyVisibility
|
private val CheckerContext.containingPropertyVisibility
|
||||||
get() = (this.containingDeclarations.last() as? FirProperty)?.visibility
|
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 }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-10
@@ -6,20 +6,20 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
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.contracts.description.EventOccurrencesRange
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElement
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker
|
import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo
|
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection
|
import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.traverse
|
import org.jetbrains.kotlin.fir.analysis.cfa.traverse
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
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.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
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
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
|
||||||
|
|
||||||
@@ -50,8 +50,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
val source = symbol.getValOrVarSource
|
val source = symbol.getValOrVarSource
|
||||||
if (symbol.callableId.callableName.asString() == "<destruct>") {
|
if (symbol.callableId.callableName.asString() == "<destruct>") {
|
||||||
lastDestructuringSource = symbol.getValOrVarSource
|
lastDestructuringSource = symbol.getValOrVarSource
|
||||||
val childrenCount = symbol.fir.psi?.children?.size ?: continue
|
lastDestructuredVariables = symbol.getDestructuringChildrenCount() ?: continue
|
||||||
lastDestructuredVariables = childrenCount - 1 // -1 cuz we don't need expression node after equals operator
|
|
||||||
destructuringCanBeVal = true
|
destructuringCanBeVal = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
destructuringCanBeVal = false
|
destructuringCanBeVal = false
|
||||||
}
|
}
|
||||||
lastDestructuredVariables--
|
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)
|
reporter.report(source, FirErrors.CAN_BE_VAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,32 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val FirPropertySymbol.getValOrVarSource
|
private val FirPropertySymbol.getValOrVarSource
|
||||||
get() = (fir.psi as? KtProperty)?.valOrVarKeyword?.toFirPsiSourceElement()
|
get() = when (fir.source) {
|
||||||
?: fir.psi?.firstChild?.toFirPsiSourceElement()
|
is FirSourceElement -> {
|
||||||
?: fir.source
|
(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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+17
@@ -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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user