diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSemicolonInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSemicolonInspection.kt index 28ac4748972..dbf94e1a8a8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSemicolonInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSemicolonInspection.kt @@ -86,20 +86,30 @@ class RedundantSemicolonInspection : AbstractKotlinInspection(), CleanupLocalIns return false // case with statement starting with '{' and call on the previous line } + return !isSemicolonAllowed(semicolon) + } + + private fun isSemicolonAllowed(semicolon: PsiElement): Boolean { if (isRequiredForCompanion(semicolon)) { - return false + return true } val prevSibling = semicolon.getPrevSiblingIgnoringWhitespaceAndComments() val nextSibling = semicolon.getNextSiblingIgnoringWhitespaceAndComments() - if (prevSibling.safeAs()?.text in softModifierKeywords && - nextSibling is KtDeclaration - ) return false - if (nextSibling.safeAs()?.operationToken == KtTokens.EXCL && - semicolon.prevLeaf()?.getStrictParentOfType() != null - ) return false - return true + if (prevSibling.safeAs()?.text in softModifierKeywords && nextSibling is KtDeclaration) { + // enum; class Foo + return false + } + + if (nextSibling is KtPrefixExpression && nextSibling.operationToken == KtTokens.EXCL) { + val typeElement = semicolon.prevLeaf()?.getStrictParentOfType()?.typeElement + if (typeElement != null) { + return typeElement !is KtNullableType // trailing '?' fixes parsing + } + } + + return false } private fun isRequiredForCompanion(semicolon: PsiElement): Boolean { diff --git a/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt b/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt new file mode 100644 index 00000000000..b20ee3ad39b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt @@ -0,0 +1,4 @@ +fun test(a: Any) { + val b = a as Boolean?; + !b +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt.after b/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt.after new file mode 100644 index 00000000000..3e2b188be45 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt.after @@ -0,0 +1,4 @@ +fun test(a: Any) { + val b = a as Boolean? + !b +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 05d31238d02..687f52f1ba9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -8396,6 +8396,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/redundantSemicolon/beforeKDocAndLambda.kt"); } + @TestMetadata("betweenNullableTypeAndNotOperator.kt") + public void testBetweenNullableTypeAndNotOperator() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSemicolon/betweenNullableTypeAndNotOperator.kt"); + } + @TestMetadata("betweenSoftKeywordAndDeclaration.kt") public void testBetweenSoftKeywordAndDeclaration() throws Exception { runTest("idea/testData/inspectionsLocal/redundantSemicolon/betweenSoftKeywordAndDeclaration.kt");