[FE] Fix positioning strategy for SAFE_CALL_WILL_CHANGE_NULLABILITY warning
^KT-46860
This commit is contained in:
committed by
teamcity
parent
565c61a702
commit
078acb1bfe
+6
@@ -21176,6 +21176,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
|
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
|
@Test
|
||||||
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
||||||
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
||||||
|
|||||||
+6
@@ -21176,6 +21176,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
|
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
|
@Test
|
||||||
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
||||||
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
||||||
|
|||||||
+6
@@ -21176,6 +21176,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
|
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
|
@Test
|
||||||
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
||||||
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
||||||
|
|||||||
+1
-1
@@ -1113,7 +1113,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val UNNECESSARY_SAFE_CALL by warning<PsiElement>(PositioningStrategy.SAFE_ACCESS) {
|
val UNNECESSARY_SAFE_CALL by warning<PsiElement>(PositioningStrategy.SAFE_ACCESS) {
|
||||||
parameter<ConeKotlinType>("receiverType")
|
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 UNEXPECTED_SAFE_CALL by error<PsiElement>(PositioningStrategy.SAFE_ACCESS)
|
||||||
val UNNECESSARY_NOT_NULL_ASSERTION by warning<KtExpression>(PositioningStrategy.OPERATOR) {
|
val UNNECESSARY_NOT_NULL_ASSERTION by warning<KtExpression>(PositioningStrategy.OPERATOR) {
|
||||||
parameter<ConeKotlinType>("receiverType")
|
parameter<ConeKotlinType>("receiverType")
|
||||||
|
|||||||
+1
-1
@@ -113,7 +113,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
|
|||||||
DECLARATION_START_TO_NAME,
|
DECLARATION_START_TO_NAME,
|
||||||
REDUNDANT_NULLABLE,
|
REDUNDANT_NULLABLE,
|
||||||
INLINE_FUN_MODIFIER,
|
INLINE_FUN_MODIFIER,
|
||||||
|
CALL_ELEMENT_WITH_DOT,
|
||||||
;
|
;
|
||||||
|
|
||||||
val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}"
|
val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}"
|
||||||
|
|||||||
+1
-1
@@ -592,7 +592,7 @@ object FirErrors {
|
|||||||
val UNSAFE_OPERATOR_CALL by error3<KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val UNSAFE_OPERATOR_CALL by error3<KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
val ITERATOR_ON_NULLABLE by error0<KtExpression>()
|
val ITERATOR_ON_NULLABLE by error0<KtExpression>()
|
||||||
val UNNECESSARY_SAFE_CALL by warning1<PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.SAFE_ACCESS)
|
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 UNEXPECTED_SAFE_CALL by error0<PsiElement>(SourceElementPositioningStrategies.SAFE_ACCESS)
|
||||||
val UNNECESSARY_NOT_NULL_ASSERTION by warning1<KtExpression, ConeKotlinType>(SourceElementPositioningStrategies.OPERATOR)
|
val UNNECESSARY_NOT_NULL_ASSERTION by warning1<KtExpression, ConeKotlinType>(SourceElementPositioningStrategies.OPERATOR)
|
||||||
val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||||
|
|||||||
+24
@@ -1120,6 +1120,30 @@ object LightTreePositioningStrategies {
|
|||||||
return markElement(byKeyword, startOffset, endOffset, tree, node)
|
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 =
|
fun KtSourceElement.hasValOrVar(): Boolean =
|
||||||
|
|||||||
+19
@@ -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
|
@JvmField
|
||||||
val DECLARATION_WITH_BODY: PositioningStrategy<KtDeclarationWithBody> = object : PositioningStrategy<KtDeclarationWithBody>() {
|
val DECLARATION_WITH_BODY: PositioningStrategy<KtDeclarationWithBody> = object : PositioningStrategy<KtDeclarationWithBody>() {
|
||||||
override fun mark(element: KtDeclarationWithBody): List<TextRange> {
|
override fun mark(element: KtDeclarationWithBody): List<TextRange> {
|
||||||
|
|||||||
+5
@@ -364,4 +364,9 @@ object SourceElementPositioningStrategies {
|
|||||||
LightTreePositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD,
|
LightTreePositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD,
|
||||||
PositioningStrategies.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_INFIX_CALL = DiagnosticFactory3.create(ERROR);
|
||||||
DiagnosticFactory3<KtExpression, PsiElement, String, PsiElement> UNSAFE_OPERATOR_CALL = DiagnosticFactory3.create(ERROR);
|
DiagnosticFactory3<KtExpression, PsiElement, String, PsiElement> UNSAFE_OPERATOR_CALL = DiagnosticFactory3.create(ERROR);
|
||||||
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
|
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);
|
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING);
|
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING);
|
||||||
DiagnosticFactory0<PsiElement> NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<PsiElement> NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
|
|
||||||
fun ff() {
|
fun ff() {
|
||||||
val i: Int = 1
|
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)<!>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ fun foo(): String {
|
|||||||
}
|
}
|
||||||
fun bar(): String {
|
fun bar(): String {
|
||||||
val x = fn() ?: return ""
|
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
|
return y
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ fun foo(): String {
|
|||||||
}
|
}
|
||||||
fun bar(): String {
|
fun bar(): String {
|
||||||
val x = fn() ?: return ""
|
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<!>
|
<!UNREACHABLE_CODE!>return y<!>
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -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
|
||||||
|
}
|
||||||
+13
@@ -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
|
||||||
|
}
|
||||||
Generated
+6
@@ -21182,6 +21182,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/nullableTypes/safeAccessOnUnit.kt");
|
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
|
@Test
|
||||||
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
|
||||||
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user