diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index ec985ff4a6e9..d8e16c2db655 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -92,13 +92,15 @@ public class HighlightUtil { static { ourClassIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet(PsiModifier.FINAL)); - ourClassIncompatibleModifiers.put(PsiModifier.FINAL, ContainerUtil.newTroveSet(PsiModifier.ABSTRACT)); + ourClassIncompatibleModifiers.put(PsiModifier.FINAL, ContainerUtil.newTroveSet(PsiModifier.ABSTRACT, PsiModifier.SEALED, PsiModifier.NON_SEALED)); ourClassIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, ContainerUtil.newTroveSet(PsiModifier.PRIVATE, PsiModifier.PUBLIC, PsiModifier.PROTECTED)); ourClassIncompatibleModifiers.put(PsiModifier.PRIVATE, ContainerUtil.newTroveSet(PsiModifier.PACKAGE_LOCAL, PsiModifier.PUBLIC, PsiModifier.PROTECTED)); ourClassIncompatibleModifiers.put(PsiModifier.PUBLIC, ContainerUtil.newTroveSet(PsiModifier.PACKAGE_LOCAL, PsiModifier.PRIVATE, PsiModifier.PROTECTED)); ourClassIncompatibleModifiers.put(PsiModifier.PROTECTED, ContainerUtil.newTroveSet(PsiModifier.PACKAGE_LOCAL, PsiModifier.PUBLIC, PsiModifier.PRIVATE)); ourClassIncompatibleModifiers.put(PsiModifier.STRICTFP, Collections.emptySet()); ourClassIncompatibleModifiers.put(PsiModifier.STATIC, Collections.emptySet()); + ourClassIncompatibleModifiers.put(PsiModifier.SEALED, ContainerUtil.newTroveSet(PsiModifier.FINAL, PsiModifier.NON_SEALED)); + ourClassIncompatibleModifiers.put(PsiModifier.NON_SEALED, ContainerUtil.newTroveSet(PsiModifier.FINAL, PsiModifier.SEALED)); ourInterfaceIncompatibleModifiers.put(PsiModifier.ABSTRACT, Collections.emptySet()); ourInterfaceIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, ContainerUtil.newTroveSet(PsiModifier.PRIVATE, PsiModifier.PUBLIC, PsiModifier.PROTECTED)); @@ -107,6 +109,8 @@ public class HighlightUtil { ourInterfaceIncompatibleModifiers.put(PsiModifier.PROTECTED, ContainerUtil.newTroveSet(PsiModifier.PACKAGE_LOCAL, PsiModifier.PUBLIC, PsiModifier.PRIVATE)); ourInterfaceIncompatibleModifiers.put(PsiModifier.STRICTFP, Collections.emptySet()); ourInterfaceIncompatibleModifiers.put(PsiModifier.STATIC, Collections.emptySet()); + ourInterfaceIncompatibleModifiers.put(PsiModifier.SEALED, ContainerUtil.newTroveSet(PsiModifier.NON_SEALED)); + ourInterfaceIncompatibleModifiers.put(PsiModifier.NON_SEALED, ContainerUtil.newTroveSet(PsiModifier.SEALED)); ourMethodIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet( PsiModifier.NATIVE, PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PRIVATE, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED, PsiModifier.DEFAULT)); @@ -3131,7 +3135,7 @@ public class HighlightUtil { if (refGrandParent instanceof PsiTypeParameter) { highlightInfo = GenericsHighlightUtil.checkElementInTypeParameterExtendsList(referenceList, (PsiClass)refGrandParent, resolveResult, ref); } - else { + else if (referenceList.equals(((PsiClass)refGrandParent).getImplementsList()) || referenceList.equals(((PsiClass)refGrandParent).getExtendsList())) { highlightInfo = HighlightClassUtil.checkExtendsClassAndImplementsInterface(referenceList, resolveResult, ref); if (highlightInfo == null) { highlightInfo = HighlightClassUtil.checkCannotInheritFromFinal(aClass, ref); diff --git a/java/java-psi-api/src/com/intellij/psi/PsiModifier.java b/java/java-psi-api/src/com/intellij/psi/PsiModifier.java index 629d35d116c6..ef07148bce77 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiModifier.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiModifier.java @@ -36,13 +36,15 @@ public interface PsiModifier { String DEFAULT = "default"; String OPEN = "open"; String TRANSITIVE = "transitive"; + String SEALED = "sealed"; + String NON_SEALED = "non-sealed"; String[] MODIFIERS = { - PUBLIC, PROTECTED, PRIVATE, STATIC, ABSTRACT, FINAL, NATIVE, SYNCHRONIZED, STRICTFP, TRANSIENT, VOLATILE, DEFAULT, OPEN, TRANSITIVE + PUBLIC, PROTECTED, PRIVATE, STATIC, ABSTRACT, FINAL, NATIVE, SYNCHRONIZED, STRICTFP, TRANSIENT, VOLATILE, DEFAULT, OPEN, TRANSITIVE, SEALED, NON_SEALED }; @MagicConstant(stringValues = { - PUBLIC, PROTECTED, PRIVATE, STATIC, ABSTRACT, FINAL, NATIVE, SYNCHRONIZED, STRICTFP, TRANSIENT, VOLATILE, DEFAULT, OPEN, TRANSITIVE, PACKAGE_LOCAL + PUBLIC, PROTECTED, PRIVATE, STATIC, ABSTRACT, FINAL, NATIVE, SYNCHRONIZED, STRICTFP, TRANSIENT, VOLATILE, DEFAULT, OPEN, TRANSITIVE, PACKAGE_LOCAL, SEALED, NON_SEALED }) @interface ModifierConstant { } } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/cache/ModifierFlags.java b/java/java-psi-impl/src/com/intellij/psi/impl/cache/ModifierFlags.java index d3075bc33005..cefade8acb3c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/cache/ModifierFlags.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/cache/ModifierFlags.java @@ -31,6 +31,8 @@ public final class ModifierFlags { public static final int PACKAGE_LOCAL_MASK = 0x1000; public static final int OPEN_MASK = 0x2000; public static final int TRANSITIVE_MASK = 0x4000; + public static final int SEALED_MASK = 0x8000; + public static final int NON_SEALED_MASK = 0x10000; public static final Object2IntMap NAME_TO_MODIFIER_FLAG_MAP = new Object2IntOpenHashMap<>(); public static final Int2ObjectMap MODIFIER_FLAG_TO_NAME_MAP = new Int2ObjectOpenHashMap<>(); @@ -51,6 +53,8 @@ public final class ModifierFlags { NAME_TO_MODIFIER_FLAG_MAP.put(PsiModifier.PACKAGE_LOCAL, PACKAGE_LOCAL_MASK); NAME_TO_MODIFIER_FLAG_MAP.put(PsiModifier.OPEN, OPEN_MASK); NAME_TO_MODIFIER_FLAG_MAP.put(PsiModifier.TRANSITIVE, TRANSITIVE_MASK); + NAME_TO_MODIFIER_FLAG_MAP.put(PsiModifier.SEALED, SEALED_MASK); + NAME_TO_MODIFIER_FLAG_MAP.put(PsiModifier.NON_SEALED, NON_SEALED_MASK); for (Object name : NAME_TO_MODIFIER_FLAG_MAP.keySet()) { MODIFIER_FLAG_TO_NAME_MAP.put(NAME_TO_MODIFIER_FLAG_MAP.getInt(name), (String)name); @@ -70,6 +74,8 @@ public final class ModifierFlags { KEYWORD_TO_MODIFIER_FLAG_MAP.put(JavaTokenType.STRICTFP_KEYWORD, STRICTFP_MASK); KEYWORD_TO_MODIFIER_FLAG_MAP.put(JavaTokenType.OPEN_KEYWORD, OPEN_MASK); KEYWORD_TO_MODIFIER_FLAG_MAP.put(JavaTokenType.TRANSITIVE_KEYWORD, TRANSITIVE_MASK); + KEYWORD_TO_MODIFIER_FLAG_MAP.put(JavaTokenType.SEALED_KEYWORD, SEALED_MASK); + KEYWORD_TO_MODIFIER_FLAG_MAP.put(JavaTokenType.NON_SEALED_KEYWORD, NON_SEALED_MASK); } public static boolean hasModifierProperty(String name, int mask) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java index 5095bbcc6dbc..99e9a5175141 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java @@ -47,6 +47,8 @@ public class PsiModifierListImpl extends JavaStubPsiElement NAME_TO_KEYWORD_TYPE_MAP.put(DEFAULT, JavaTokenType.DEFAULT_KEYWORD); NAME_TO_KEYWORD_TYPE_MAP.put(OPEN, JavaTokenType.OPEN_KEYWORD); NAME_TO_KEYWORD_TYPE_MAP.put(TRANSITIVE, JavaTokenType.TRANSITIVE_KEYWORD); + NAME_TO_KEYWORD_TYPE_MAP.put(SEALED, JavaTokenType.SEALED_KEYWORD); + NAME_TO_KEYWORD_TYPE_MAP.put(NON_SEALED, JavaTokenType.NON_SEALED_KEYWORD); KEYWORD_TYPE_TO_NAME_MAP = new THashMap<>(); for (String name : NAME_TO_KEYWORD_TYPE_MAP.keySet()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingSealedTypes/SealedTypesBasics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingSealedTypes/SealedTypesBasics.java new file mode 100644 index 000000000000..43a98903a8d5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingSealedTypes/SealedTypesBasics.java @@ -0,0 +1,15 @@ +//classes +sealed class A permits B {} +sealed class B extends A permits C, D {} +final class C extends B {} +non-sealed class D extends B {} +class E extends A {} +sealed sealed class SealedSealed {} +sealed non-sealed class SealedNonSealed {} +sealed final class SealedFinal {} + +//interfaces +sealed interface IA permits IB, IC {} +final class IB implements IA {} +sealed interface IC extends IA {} +class ICSameFile implements IC {} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightSealedTypesHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightSealedTypesHighlightingTest.java new file mode 100644 index 000000000000..974495ff19a5 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightSealedTypesHighlightingTest.java @@ -0,0 +1,29 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.daemon; + +import com.intellij.JavaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public class LightSealedTypesHighlightingTest extends LightJavaCodeInsightFixtureTestCase { + @Override + protected String getBasePath() { + return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlightingSealedTypes"; + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_15; + } + + public void testSealedTypesBasics() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile(getTestName(false) + ".java"); + myFixture.checkHighlighting(); + } +} \ No newline at end of file