diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 991d6b49b59..87be5104a58 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -9451,6 +9451,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/inference.kt"); } + @Test + @TestMetadata("isAsOperators.kt") + public void testIsAsOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt"); + } + + @Test + @TestMetadata("isAsOperatorsEnabled.kt") + public void testIsAsOperatorsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt"); + } + @Test @TestMetadata("notApplicable.kt") public void testNotApplicable() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 55fafac46d3..4ecee2e84ad 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -9451,6 +9451,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/inference.kt"); } + @Test + @TestMetadata("isAsOperators.kt") + public void testIsAsOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt"); + } + + @Test + @TestMetadata("isAsOperatorsEnabled.kt") + public void testIsAsOperatorsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt"); + } + @Test @TestMetadata("notApplicable.kt") public void testNotApplicable() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index fdbd9e8dfec..d3743f5eac7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -196,6 +196,8 @@ public interface Errors { DiagnosticFactory1 EXPANDED_TYPE_CANNOT_BE_CONSTRUCTED = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 EXPANDED_TYPE_CANNOT_BE_INHERITED = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 DEPRECATED_SYNTAX_WITH_DEFINITELY_NOT_NULL = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 MODIFIER_LIST_NOT_ALLOWED = DiagnosticFactory0.create(ERROR); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 1b36e5d980d..147fab671eb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -601,6 +601,7 @@ public class DefaultErrorMessages { MAP.put(UNUSED_TYPEALIAS_PARAMETER, "Type alias parameter {0} is not used in the expanded type {1} and does not affect type checking", NAME, RENDER_TYPE); MAP.put(EXPANDED_TYPE_CANNOT_BE_CONSTRUCTED, "Expanded type {0} contains non-invariant projections in top-level arguments and cannot be constructed", RENDER_TYPE); MAP.put(EXPANDED_TYPE_CANNOT_BE_INHERITED, "Expanded type {0} contains non-invariant projections in top-level arguments and cannot be inherited from", RENDER_TYPE); + MAP.put(DEPRECATED_SYNTAX_WITH_DEFINITELY_NOT_NULL, "Applying ''!!'' to the whole as/is expression without parentheses is deprecated. Please, put parentheses explicitly"); MAP.put(MODIFIER_LIST_NOT_ALLOWED, "Modifiers and annotations are not allowed here, because there are other modifiers or annotations outside of parenthesis"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 4970b3b4538..90cdc191bf4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -293,6 +293,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return facade.getTypeInfo(left, contextWithNoExpectedType).clearType(); } + DefinitelyNotNullDeprecationKt.reportDeprecatedDefinitelyNotNullSyntax(expression, right, context); + IElementType operationType = expression.getOperationReference().getReferencedNameElementType(); boolean allowBareTypes = BARE_TYPES_ALLOWED.contains(operationType); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 5a54e421271..9e1e6bb5a71 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -60,6 +60,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo) } + expression.reportDeprecatedDefinitelyNotNullSyntax(expression.typeReference, contextWithExpectedType) + val resultTypeInfo = components.dataFlowAnalyzer.checkType( typeInfo.replaceType(components.builtIns.booleanType), expression, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/definitelyNotNullDeprecation.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/definitelyNotNullDeprecation.kt new file mode 100644 index 00000000000..221f108ab6c --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/definitelyNotNullDeprecation.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2021 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.types.expressions + +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtOperationExpression +import org.jetbrains.kotlin.psi.KtPostfixExpression +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.psi.KtUserType +import org.jetbrains.kotlin.psi.psiUtil.nextLeaf + +fun KtOperationExpression.reportDeprecatedDefinitelyNotNullSyntax( + rhs: KtTypeReference?, + context: ExpressionTypingContext +) { + val nextLeaf = nextLeaf() + if (nextLeaf is LeafPsiElement && nextLeaf.elementType === KtTokens.EXCLEXCL && rhs?.typeElement is KtUserType) { + val parent = PsiTreeUtil.findCommonParent(nextLeaf, this) + if (parent is KtPostfixExpression && parent.operationToken === KtTokens.EXCLEXCL) { + context.trace.report(Errors.DEPRECATED_SYNTAX_WITH_DEFINITELY_NOT_NULL.on((parent as KtPostfixExpression?)!!)) + } + } +} diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java index c353d90ad1c..b77d8e7dc63 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java +++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java @@ -158,7 +158,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { AS(AS_KEYWORD, AS_SAFE) { @Override public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) { - parser.myKotlinParsing.parseTypeRef(); + parser.myKotlinParsing.parseTypeRefWithoutDefinitelyNotNull(); return BINARY_WITH_TYPE; } @@ -177,7 +177,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { @Override public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) { if (operation == IS_KEYWORD || operation == NOT_IS) { - parser.myKotlinParsing.parseTypeRef(); + parser.myKotlinParsing.parseTypeRefWithoutDefinitelyNotNull(); return IS_EXPRESSION; } diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index 7e05974e7e8..cac71af60bf 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -833,7 +833,7 @@ public class KotlinParsing extends AbstractKotlinParsing { PsiBuilder.Marker reference = mark(); PsiBuilder.Marker typeReference = mark(); - parseUserType(/* forAnnotation */ true); + parseUserType(/* allowNotNullTypeParameter */ false); typeReference.done(TYPE_REFERENCE); reference.done(CONSTRUCTOR_CALLEE); @@ -2055,14 +2055,22 @@ public class KotlinParsing extends AbstractKotlinParsing { parseTypeRef(TokenSet.EMPTY); } + void parseTypeRefWithoutDefinitelyNotNull() { + parseTypeRef(TokenSet.EMPTY, /* allowNotNullTypeParameters */ false); + } + void parseTypeRef(TokenSet extraRecoverySet) { - PsiBuilder.Marker typeRefMarker = parseTypeRefContents(extraRecoverySet); + parseTypeRef(extraRecoverySet, /* allowNotNullTypeParameters */ true); + } + + private void parseTypeRef(TokenSet extraRecoverySet, boolean allowNotNullTypeParameters) { + PsiBuilder.Marker typeRefMarker = parseTypeRefContents(extraRecoverySet, allowNotNullTypeParameters); typeRefMarker.done(TYPE_REFERENCE); } // The extraRecoverySet is needed for the foo(bar(z)) case, to tell whether we should stop // on expression-indicating symbols or not - private PsiBuilder.Marker parseTypeRefContents(TokenSet extraRecoverySet) { + private PsiBuilder.Marker parseTypeRefContents(TokenSet extraRecoverySet, boolean allowNotNullTypeParameters) { PsiBuilder.Marker typeRefMarker = mark(); parseTypeModifierList(); @@ -2078,14 +2086,14 @@ public class KotlinParsing extends AbstractKotlinParsing { dynamicType.done(DYNAMIC_TYPE); } else if (at(IDENTIFIER) || at(PACKAGE_KEYWORD) || atParenthesizedMutableForPlatformTypes(0)) { - parseUserType(/* forAnnotation */ false); + parseUserType(allowNotNullTypeParameters); } else if (at(LPAR)) { PsiBuilder.Marker functionOrParenthesizedType = mark(); // This may be a function parameter list or just a parenthesized type advance(); // LPAR - parseTypeRefContents(TokenSet.EMPTY).drop(); // parenthesized types, no reference element around it is needed + parseTypeRefContents(TokenSet.EMPTY, allowNotNullTypeParameters).drop(); // parenthesized types, no reference element around it is needed if (at(RPAR)) { advance(); // RPAR @@ -2175,7 +2183,7 @@ public class KotlinParsing extends AbstractKotlinParsing { * - (Mutable)List! * - Array<(out) Foo>! */ - private void parseUserType(boolean forAnnotation) { + private void parseUserType(boolean allowNotNullTypeParameter) { PsiBuilder.Marker userType = mark(); if (at(PACKAGE_KEYWORD)) { @@ -2221,7 +2229,7 @@ public class KotlinParsing extends AbstractKotlinParsing { userType.done(USER_TYPE); - if (!forAnnotation && at(EXCLEXCL)) { + if (allowNotNullTypeParameter && at(EXCLEXCL)) { PsiBuilder.Marker definitelyNotNull = userType.precede(); advance(); // !! definitelyNotNull.done(DEFINITELY_NOT_NULL_TYPE); diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.fir.kt new file mode 100644 index 00000000000..2f55c6a1114 --- /dev/null +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.fir.kt @@ -0,0 +1,16 @@ +// SKIP_TXT +// !LANGUAGE: -DefinitelyNotNullTypeParameters + +fun Any.bar() {} +fun Boolean.baz() {} + +var x: Int = 0 + +inline fun foo(v: Any?): T { + if (x > 0) 1 else v as T!! + if (x > 1) 2 else v as? T!! + if (x > 2) 3 else v is T!! + if (x > 3) 4 else v !is T!! + + return v as T!! +} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt new file mode 100644 index 00000000000..e6b33f855c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt @@ -0,0 +1,16 @@ +// SKIP_TXT +// !LANGUAGE: -DefinitelyNotNullTypeParameters + +fun Any.bar() {} +fun Boolean.baz() {} + +var x: Int = 0 + +inline fun foo(v: Any?): T { + if (x > 0) 1 else v as T!! + if (x > 1) 2 else v as? T!! + if (x > 2) 3 else v is T!! + if (x > 3) 4 else v !is T!! + + return v as T!! +} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.fir.kt new file mode 100644 index 00000000000..0604ae01251 --- /dev/null +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.fir.kt @@ -0,0 +1,16 @@ +// SKIP_TXT +// !LANGUAGE: +DefinitelyNotNullTypeParameters + +fun Any.bar() {} +fun Boolean.baz() {} + +var x: Int = 0 + +inline fun foo(v: Any?): T { + if (x > 0) 1 else v as T!! + if (x > 1) 2 else v as? T!! + if (x > 2) 3 else v is T!! + if (x > 3) 4 else v !is T!! + + return v as T!! +} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt new file mode 100644 index 00000000000..a55482d97ba --- /dev/null +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt @@ -0,0 +1,16 @@ +// SKIP_TXT +// !LANGUAGE: +DefinitelyNotNullTypeParameters + +fun Any.bar() {} +fun Boolean.baz() {} + +var x: Int = 0 + +inline fun foo(v: Any?): T { + if (x > 0) 1 else v as T!! + if (x > 1) 2 else v as? T!! + if (x > 2) 3 else v is T!! + if (x > 3) 4 else v !is T!! + + return v as T!! +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 65f1629de19..c022e33e833 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -9457,6 +9457,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/inference.kt"); } + @Test + @TestMetadata("isAsOperators.kt") + public void testIsAsOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt"); + } + + @Test + @TestMetadata("isAsOperatorsEnabled.kt") + public void testIsAsOperatorsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt"); + } + @Test @TestMetadata("notApplicable.kt") public void testNotApplicable() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 2f1911addeb..283027ce8ad 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -9451,6 +9451,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/inference.kt"); } + @Test + @TestMetadata("isAsOperators.kt") + public void testIsAsOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperators.kt"); + } + + @Test + @TestMetadata("isAsOperatorsEnabled.kt") + public void testIsAsOperatorsEnabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/explicitDefinitelyNotNull/isAsOperatorsEnabled.kt"); + } + @Test @TestMetadata("notApplicable.kt") public void testNotApplicable() throws Exception {