package com.intellij.codeInspection.tests.java import com.intellij.jvm.analysis.internal.testFramework.UnstableApiUsageInspectionTestBase import com.intellij.jvm.analysis.testFramework.JvmLanguage class JavaUnstableApiUsageInspectionTest : UnstableApiUsageInspectionTestBase() { fun `test java experimental api usages`() { inspection.myIgnoreInsideImports = false myFixture.testHighlighting(JvmLanguage.JAVA, """ import experimental.pkg.AnnotatedClass; import experimental.pkg.ClassWithExperimentalTypeInSignature; import experimental.pkg.OwnerOfMembersWithExperimentalTypesInSignature; import static experimental.pkg.AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static experimental.pkg.AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass; import static experimental.pkg.AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static experimental.pkg.AnnotatedClass.staticAnnotatedMethodInAnnotatedClass; import experimental.pkg.NonAnnotatedClass; import static experimental.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static experimental.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass; import static experimental.pkg.NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static experimental.pkg.NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass; import experimental.pkg.AnnotatedEnum; import experimental.pkg.NonAnnotatedEnum; import static experimental.pkg.AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static experimental.pkg.AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static experimental.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import static experimental.pkg.NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import experimental.pkg.AnnotatedAnnotation; import experimental.pkg.NonAnnotatedAnnotation; import experimental.annotatedPkg.ClassInAnnotatedPkg; class UnstableElementsTest { public void test() { String s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass(); AnnotatedClass annotatedClassInstanceViaNonAnnotatedConstructor = new AnnotatedClass(); s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass; annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass(); s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; staticNonAnnotatedMethodInAnnotatedClass(); s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; AnnotatedClass.staticAnnotatedMethodInAnnotatedClass(); AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass(""); s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass; annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass(); s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; staticAnnotatedMethodInAnnotatedClass(); // --------------------------------- s = NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass(); NonAnnotatedClass nonAnnotatedClassInstanceViaNonAnnotatedConstructor = new NonAnnotatedClass(); s = nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInNonAnnotatedClass; nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInNonAnnotatedClass(); s = NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; staticNonAnnotatedMethodInNonAnnotatedClass(); s = NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass(); NonAnnotatedClass nonAnnotatedClassInstanceViaAnnotatedConstructor = new NonAnnotatedClass(""); s = nonAnnotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInNonAnnotatedClass; nonAnnotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInNonAnnotatedClass(); s = ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; staticAnnotatedMethodInNonAnnotatedClass(); // --------------------------------- AnnotatedEnum nonAnnotatedValueInAnnotatedEnum = AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; nonAnnotatedValueInAnnotatedEnum = NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; AnnotatedEnum annotatedValueInAnnotatedEnum = AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM; annotatedValueInAnnotatedEnum = ANNOTATED_VALUE_IN_ANNOTATED_ENUM; NonAnnotatedEnum nonAnnotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; nonAnnotatedValueInNonAnnotatedEnum = NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; NonAnnotatedEnum annotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; annotatedValueInNonAnnotatedEnum = ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; // --------------------------------- @AnnotatedAnnotation class C1 {} @AnnotatedAnnotation(nonAnnotatedAttributeInAnnotatedAnnotation = "123") class C2 {} @AnnotatedAnnotation(annotatedAttributeInAnnotatedAnnotation = "123") class C3 {} @NonAnnotatedAnnotation class C4 {} @NonAnnotatedAnnotation(nonAnnotatedAttributeInNonAnnotatedAnnotation = "123") class C5 {} @NonAnnotatedAnnotation(annotatedAttributeInNonAnnotatedAnnotation = "123") class C6 {} } } class DirectOverrideAnnotatedMethod extends NonAnnotatedClass { @Override public void annotatedMethodInNonAnnotatedClass() {} } class IndirectOverrideAnnotatedMethod extends DirectOverrideAnnotatedMethod { @Override public void annotatedMethodInNonAnnotatedClass() {} } class DirectOverrideNonAnnotatedMethodInAnnotatedClass extends AnnotatedClass { @Override public void nonAnnotatedMethodInAnnotatedClass() {} } class DirectOverrideAnnotatedMethodInAnnotatedClass extends AnnotatedClass { @Override public void annotatedMethodInAnnotatedClass() {} } //No warning should be produced. class WarningsOfExperimentalTypesInSignature { public void classUsage() { new ClassWithExperimentalTypeInSignature<AnnotatedClass>(); } public void membersUsages(OwnerOfMembersWithExperimentalTypesInSignature owner) { Object field = owner.field; owner.parameterType(null); owner.returnType(); Object fieldPkg = owner.field; owner.parameterTypePkg(null); owner.returnTypePkg(); } } """.trimIndent()) } fun `test java do not report unstable api usages inside import statements`() { inspection.myIgnoreInsideImports = true myFixture.testHighlighting(JvmLanguage.JAVA, """ import experimental.pkg.AnnotatedClass; import static experimental.pkg.AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static experimental.pkg.AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass; import static experimental.pkg.AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static experimental.pkg.AnnotatedClass.staticAnnotatedMethodInAnnotatedClass; import experimental.pkg.NonAnnotatedClass; import static experimental.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static experimental.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass; import static experimental.pkg.NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static experimental.pkg.NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass; import experimental.pkg.AnnotatedEnum; import experimental.pkg.NonAnnotatedEnum; import static experimental.pkg.AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static experimental.pkg.AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static experimental.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import static experimental.pkg.NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import experimental.pkg.AnnotatedAnnotation; import experimental.pkg.NonAnnotatedAnnotation; import experimental.annotatedPkg.ClassInAnnotatedPkg; """.trimIndent()) } fun `test java no warnings on access to members of the same file`() { myFixture.testHighlighting(JvmLanguage.JAVA, """ package test; import experimental.pkg.AnnotatedClass; @SuppressWarnings({"SameParameterValue", "unused", "UnusedReturnValue", "ResultOfMethodCallIgnored", "MethodMayBeStatic", "ObjectToString"}) class NoWarningsMembersOfTheSameFile { private AnnotatedClass field; private static AnnotatedClass staticField; private AnnotatedClass returnType() { return null; } private void paramType(AnnotatedClass param) { } private static AnnotatedClass staticReturnType() { return null; } private static void staticParamType(AnnotatedClass a) { } void testNoWarningsProducedForMembersOfTheSameClass() { field.toString(); staticField.toString(); returnType(); paramType(null); staticReturnType(); staticParamType(null); } private class InnerClass { void testNoWarningsProducedForMembersEnclosingClass() { field.toString(); staticField.toString(); returnType(); paramType(null); staticReturnType(); staticParamType(null); } } } """.trimIndent()) } fun `test highlighting scheduled for removal`() { inspection.myIgnoreInsideImports = false myFixture.testHighlighting(JvmLanguage.JAVA, """ // Copyright 2000-2018 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. import scheduledForRemoval.pkg.AnnotatedClass; import scheduledForRemoval.pkg.ClassWithScheduledForRemovalTypeInSignature; import scheduledForRemoval.pkg.OwnerOfMembersWithScheduledForRemovalTypesInSignature; import static scheduledForRemoval.pkg.AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static scheduledForRemoval.pkg.AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass; import static scheduledForRemoval.pkg.AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; import static scheduledForRemoval.pkg.AnnotatedClass.staticAnnotatedMethodInAnnotatedClass; import scheduledForRemoval.pkg.NonAnnotatedClass; import static scheduledForRemoval.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static scheduledForRemoval.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass; import static scheduledForRemoval.pkg.NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; import static scheduledForRemoval.pkg.NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass; import scheduledForRemoval.pkg.AnnotatedEnum; import scheduledForRemoval.pkg.NonAnnotatedEnum; import static scheduledForRemoval.pkg.AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static scheduledForRemoval.pkg.AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM; import static scheduledForRemoval.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import static scheduledForRemoval.pkg.NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; import scheduledForRemoval.pkg.AnnotatedAnnotation; import scheduledForRemoval.pkg.NonAnnotatedAnnotation; import scheduledForRemoval.annotatedPkg.ClassInAnnotatedPkg; class ScheduledForRemovalElementsTest { public void test() { String s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass(); AnnotatedClass annotatedClassInstanceViaNonAnnotatedConstructor = new AnnotatedClass(); s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass; annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass(); s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; staticNonAnnotatedMethodInAnnotatedClass(); s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; AnnotatedClass.staticAnnotatedMethodInAnnotatedClass(); AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass(""); s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass; annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass(); s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS; staticAnnotatedMethodInAnnotatedClass(); // --------------------------------- s = NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass(); NonAnnotatedClass nonAnnotatedClassInstanceViaNonAnnotatedConstructor = new NonAnnotatedClass(); s = nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInNonAnnotatedClass; nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInNonAnnotatedClass(); s = NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; staticNonAnnotatedMethodInNonAnnotatedClass(); s = NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass(); NonAnnotatedClass nonAnnotatedClassInstanceViaAnnotatedConstructor = new NonAnnotatedClass(""); s = nonAnnotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInNonAnnotatedClass; nonAnnotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInNonAnnotatedClass(); s = ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS; staticAnnotatedMethodInNonAnnotatedClass(); // --------------------------------- AnnotatedEnum nonAnnotatedValueInAnnotatedEnum = AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; nonAnnotatedValueInAnnotatedEnum = NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM; AnnotatedEnum annotatedValueInAnnotatedEnum = AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM; annotatedValueInAnnotatedEnum = ANNOTATED_VALUE_IN_ANNOTATED_ENUM; NonAnnotatedEnum nonAnnotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; nonAnnotatedValueInNonAnnotatedEnum = NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; NonAnnotatedEnum annotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; annotatedValueInNonAnnotatedEnum = ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM; // --------------------------------- @AnnotatedAnnotation class C1 {} @AnnotatedAnnotation(nonAnnotatedAttributeInAnnotatedAnnotation = "123") class C2 {} @AnnotatedAnnotation(annotatedAttributeInAnnotatedAnnotation = "123") class C3 {} @NonAnnotatedAnnotation class C4 {} @NonAnnotatedAnnotation(nonAnnotatedAttributeInNonAnnotatedAnnotation = "123") class C5 {} @NonAnnotatedAnnotation(annotatedAttributeInNonAnnotatedAnnotation = "123") class C6 {} } } class DirectOverrideAnnotatedMethod extends NonAnnotatedClass { @Override public void annotatedMethodInNonAnnotatedClass() {} } class IndirectOverrideAnnotatedMethod extends DirectOverrideAnnotatedMethod { @Override public void annotatedMethodInNonAnnotatedClass() {} } class WarningsOfScheduledForRemovalTypesInSignature { public void classUsage() { new ClassWithScheduledForRemovalTypeInSignature<AnnotatedClass>(); } public void membersUsages(OwnerOfMembersWithScheduledForRemovalTypesInSignature owner) { Object field = owner.field; owner.parameterType(null); owner.returnType(); Object fieldPkg = owner.field; owner.parameterTypePkg(null); owner.returnTypePkg(); } } """.trimIndent()) } fun `test scheduled for removal fix`() { inspection.myIgnoreInsideImports = false inspection.myIgnoreApiDeclaredInThisProject = false myFixture.testQuickFix(JvmLanguage.JAVA, before = """ package org.jetbrains.annotations; final class ApiStatus { public @interface ScheduledForRemoval { } } class X { /** * @deprecated use {@link #bar()} */ @Deprecated @ApiStatus.ScheduledForRemoval public static void foo() { } public static void bar() { } } class Use { void test() { X.foo(); } } """.trimIndent(), after = """ package org.jetbrains.annotations; final class ApiStatus { public @interface ScheduledForRemoval { } } class X { /** * @deprecated use {@link #bar()} */ @Deprecated @ApiStatus.ScheduledForRemoval public static void foo() { } public static void bar() { } } class Use { void test() { X.bar(); } } """.trimIndent(), hint = "Replace method call with 'X.bar()'", testPreview = true) } }