[FE] Fix positioning strategy for SAFE_CALL_WILL_CHANGE_NULLABILITY warning

^KT-46860
This commit is contained in:
Dmitriy Novozhilov
2022-03-28 11:28:08 +04:00
committed by teamcity
parent 565c61a702
commit 078acb1bfe
16 changed files with 127 additions and 7 deletions
@@ -21176,6 +21176,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
}
@Test
@TestMetadata("safeCallOnNotNullableType.kt")
public void testSafeCallOnNotNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnNotNullableType.kt");
}
@Test
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
@@ -21176,6 +21176,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
}
@Test
@TestMetadata("safeCallOnNotNullableType.kt")
public void testSafeCallOnNotNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnNotNullableType.kt");
}
@Test
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
@@ -21176,6 +21176,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
}
@Test
@TestMetadata("safeCallOnNotNullableType.kt")
public void testSafeCallOnNotNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnNotNullableType.kt");
}
@Test
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
@@ -1113,7 +1113,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val UNNECESSARY_SAFE_CALL by warning<PsiElement>(PositioningStrategy.SAFE_ACCESS) {
parameter<ConeKotlinType>("receiverType")
}
val SAFE_CALL_WILL_CHANGE_NULLABILITY by warning<KtSafeQualifiedExpression>()
val SAFE_CALL_WILL_CHANGE_NULLABILITY by warning<KtSafeQualifiedExpression>(PositioningStrategy.CALL_ELEMENT_WITH_DOT)
val UNEXPECTED_SAFE_CALL by error<PsiElement>(PositioningStrategy.SAFE_ACCESS)
val UNNECESSARY_NOT_NULL_ASSERTION by warning<KtExpression>(PositioningStrategy.OPERATOR) {
parameter<ConeKotlinType>("receiverType")
@@ -113,7 +113,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
DECLARATION_START_TO_NAME,
REDUNDANT_NULLABLE,
INLINE_FUN_MODIFIER,
CALL_ELEMENT_WITH_DOT,
;
val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}"
@@ -592,7 +592,7 @@ object FirErrors {
val UNSAFE_OPERATOR_CALL by error3<KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val ITERATOR_ON_NULLABLE by error0<KtExpression>()
val UNNECESSARY_SAFE_CALL by warning1<PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.SAFE_ACCESS)
val SAFE_CALL_WILL_CHANGE_NULLABILITY by warning0<KtSafeQualifiedExpression>()
val SAFE_CALL_WILL_CHANGE_NULLABILITY by warning0<KtSafeQualifiedExpression>(SourceElementPositioningStrategies.CALL_ELEMENT_WITH_DOT)
val UNEXPECTED_SAFE_CALL by error0<PsiElement>(SourceElementPositioningStrategies.SAFE_ACCESS)
val UNNECESSARY_NOT_NULL_ASSERTION by warning1<KtExpression, ConeKotlinType>(SourceElementPositioningStrategies.OPERATOR)
val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
@@ -1120,6 +1120,30 @@ object LightTreePositioningStrategies {
return markElement(byKeyword, startOffset, endOffset, tree, node)
}
}
val CALL_ELEMENT_WITH_DOT: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
val callElementRanges = SELECTOR_BY_QUALIFIED.mark(node, startOffset, endOffset, tree)
val callElementRange = when (callElementRanges.size) {
1 -> callElementRanges.first()
else -> return callElementRanges
}
val dotRanges = SAFE_ACCESS.mark(node, startOffset, endOffset, tree)
val dotRange = when (dotRanges.size) {
1 -> dotRanges.first()
else -> return dotRanges
}
return listOf(TextRange(dotRange.startOffset, callElementRange.endOffset))
}
}
}
fun KtSourceElement.hasValOrVar(): Boolean =
@@ -499,6 +499,25 @@ object PositioningStrategies {
}
}
@JvmField
val CALL_ELEMENT_WITH_DOT: PositioningStrategy<KtQualifiedExpression> = object : PositioningStrategy<KtQualifiedExpression>() {
override fun mark(element: KtQualifiedExpression): List<TextRange> {
val callElementRanges = SELECTOR_BY_QUALIFIED.mark(element)
val callElementRange = when (callElementRanges.size) {
1 -> callElementRanges.first()
else -> return callElementRanges
}
val dotRanges = SAFE_ACCESS.mark(element)
val dotRange = when (dotRanges.size) {
1 -> dotRanges.first()
else -> return dotRanges
}
return listOf(TextRange(dotRange.startOffset, callElementRange.endOffset))
}
}
@JvmField
val DECLARATION_WITH_BODY: PositioningStrategy<KtDeclarationWithBody> = object : PositioningStrategy<KtDeclarationWithBody>() {
override fun mark(element: KtDeclarationWithBody): List<TextRange> {
@@ -364,4 +364,9 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD,
PositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD
)
val CALL_ELEMENT_WITH_DOT = SourceElementPositioningStrategy(
LightTreePositioningStrategies.CALL_ELEMENT_WITH_DOT,
PositioningStrategies.CALL_ELEMENT_WITH_DOT
)
}
@@ -1097,7 +1097,7 @@ public interface Errors {
DiagnosticFactory3<KtExpression, PsiElement, String, PsiElement> UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<KtExpression, PsiElement, String, PsiElement> UNSAFE_OPERATOR_CALL = DiagnosticFactory3.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> SAFE_CALL_WILL_CHANGE_NULLABILITY = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtQualifiedExpression> SAFE_CALL_WILL_CHANGE_NULLABILITY = DiagnosticFactory0.create(WARNING, PositioningStrategies.CALL_ELEMENT_WITH_DOT);
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
@@ -4,5 +4,5 @@
fun ff() {
val i: Int = 1
val a: Int = <!SAFE_CALL_WILL_CHANGE_NULLABILITY!>i<!UNNECESSARY_SAFE_CALL!>?.<!>plus(2)<!>
val a: Int = i<!SAFE_CALL_WILL_CHANGE_NULLABILITY!><!UNNECESSARY_SAFE_CALL!>?.<!>plus(2)<!>
}
@@ -8,6 +8,6 @@ fun foo(): String {
}
fun bar(): String {
val x = fn() ?: return ""
val y = <!SAFE_CALL_WILL_CHANGE_NULLABILITY!>x<!UNNECESSARY_SAFE_CALL!>?.<!>let { throw Exception() }<!> <!USELESS_ELVIS!>?: "unreachable"<!>
val y = x<!SAFE_CALL_WILL_CHANGE_NULLABILITY!><!UNNECESSARY_SAFE_CALL!>?.<!>let { throw Exception() }<!> <!USELESS_ELVIS!>?: "unreachable"<!>
return y
}
@@ -8,6 +8,6 @@ fun foo(): String {
}
fun bar(): String {
val x = fn() ?: return ""
<!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>y<!> =<!> <!SAFE_CALL_WILL_CHANGE_NULLABILITY!>x<!UNNECESSARY_SAFE_CALL!>?.<!>let { throw Exception() }<!> <!UNREACHABLE_CODE, USELESS_ELVIS!>?: "unreachable"<!>
<!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>y<!> =<!> x<!SAFE_CALL_WILL_CHANGE_NULLABILITY!><!UNNECESSARY_SAFE_CALL!>?.<!>let { throw Exception() }<!> <!UNREACHABLE_CODE, USELESS_ELVIS!>?: "unreachable"<!>
<!UNREACHABLE_CODE!>return y<!>
}
@@ -0,0 +1,35 @@
// FIR_IDENTICAL
// LANGUAGE: -SafeCallsAreAlwaysNullable
// DIAGNOSTICS: -UNNECESSARY_SAFE_CALL
// ISSUE: KT-46860
interface A {
fun id(): A
fun foo(): String
}
fun test_1(a: A) {
val s = a.id().id().id().id().id().id().id().id()<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.foo()<!>.length
}
fun test_2(a: A) {
val s = a.id()
.id()
.id()
.id()
<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.id()<!>
.id()
.id()
.id()
<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.foo()<!>
<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.length<!>
}
fun test_3(a: A) {
val s = a.id()<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.
id()<!>.
id()<!SAFE_CALL_WILL_CHANGE_NULLABILITY!>?.
foo()<!>.
length
}
@@ -0,0 +1,13 @@
package
public fun test_1(/*0*/ a: A): kotlin.Unit
public fun test_2(/*0*/ a: A): kotlin.Unit
public fun test_3(/*0*/ a: A): kotlin.Unit
public interface A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public abstract fun id(): A
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -21182,6 +21182,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
}
@Test
@TestMetadata("safeCallOnNotNullableType.kt")
public void testSafeCallOnNotNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnNotNullableType.kt");
}
@Test
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {