K2: Fix Java enhancement when jsr305.jar is not in classpath

In that case, when loading Java, we just remember the name of `@Nonnull`
argument (javax.annotation.meta.When), and then use it during enhancement

^KT-56656 Fixed
This commit is contained in:
Denis.Zharkov
2023-03-13 18:14:06 +01:00
committed by Space Team
parent 955b8e1490
commit 644391a8e0
14 changed files with 46 additions and 374 deletions
@@ -10,11 +10,9 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.collectEnumEntries
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildFromMissingDependenciesNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType
@@ -285,9 +283,8 @@ abstract class AbstractAnnotationDeserializer(
name = entryName
resolvedSymbol = it.symbol
}
} ?: buildErrorNamedReference {
diagnostic =
ConeSimpleDiagnostic("Strange deserialized enum value: $classId.$entryName", DiagnosticKind.DeserializationError)
} ?: buildFromMissingDependenciesNamedReference {
name = entryName
}
if (enumEntrySymbol != null) {
typeRef = enumEntrySymbol.returnTypeRef
@@ -12,8 +12,11 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.convertAnnotationsToFir
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.references.FirFromMissingDependenciesNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.load.java.AbstractAnnotationTypeQualifierResolver
import org.jetbrains.kotlin.load.java.JavaModuleAnnotationsProvider
import org.jetbrains.kotlin.load.java.JavaTypeEnhancementState
@@ -48,7 +51,16 @@ class FirAnnotationTypeQualifierResolver(
when (this) {
is FirArrayOfCall -> arguments.flatMap { it.toEnumNames() }
is FirVarargArgumentsExpression -> arguments.flatMap { it.toEnumNames() }
else -> listOfNotNull(toResolvedCallableSymbol()?.callableId?.callableName?.asString())
else -> {
val name = when (val reference = toReference()) {
is FirResolvedNamedReference ->
(reference.resolvedSymbol as? FirCallableSymbol<*>)?.callableId?.callableName?.asString()
is FirFromMissingDependenciesNamedReference -> reference.name.asString()
else -> null
}
listOfNotNull(name)
}
}
fun extractDefaultQualifiers(firClass: FirRegularClass): JavaTypeQualifiersByElementType? {
@@ -13,10 +13,15 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.buildUnaryArgumentList
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildFromMissingDependenciesNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.bindSymbolToLookupTag
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
@@ -133,7 +138,7 @@ private val JAVA_TARGETS_TO_KOTLIN = mapOf(
private fun buildEnumCall(session: FirSession, classId: ClassId?, entryName: Name?): FirPropertyAccessExpression {
return buildPropertyAccessExpression {
val calleeReference = if (classId != null && entryName != null) {
val resolvedCalleeReference: FirResolvedNamedReference? = if (classId != null && entryName != null) {
session.symbolProvider.getClassDeclaredPropertySymbols(classId, entryName)
.firstOrNull()?.let { propertySymbol ->
buildResolvedNamedReference {
@@ -144,9 +149,18 @@ private fun buildEnumCall(session: FirSession, classId: ClassId?, entryName: Nam
} else {
null
}
this.calleeReference = calleeReference ?: buildErrorNamedReference {
diagnostic = ConeSimpleDiagnostic("Strange Java enum value: $classId.$entryName", DiagnosticKind.Java)
}
this.calleeReference =
resolvedCalleeReference
// if we haven't found the containing class for the entry in the classpath, let's just remember its name, so we could use it,
// e.g. during Java enhancement
?: entryName?.let {
buildFromMissingDependenciesNamedReference {
name = entryName
}
} ?: buildErrorNamedReference {
diagnostic = ConeSimpleDiagnostic("Enum entry name is null in Java for $classId", DiagnosticKind.Java)
}
if (classId != null) {
this.typeRef = buildResolvedTypeRef {
@@ -1,77 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
// JSR305_GLOBAL_REPORT: strict
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target({ElementType.TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NonNullApi {
}
// FILE: spr/NullableApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NullableApi {
}
// FILE: A.java
import spr.*;
import java.util.*;
@NonNullApi
public class A {
public String foo(String x) { return ""; }
public @NullableApi String bar(@NullableApi String y) { return ""; }
public @NullableApi List<String> baz1() { return null; }
public List<@NullableApi String> baz2() { return null; }
public @NullableApi List<@NonNullApi String> baz3() { return null; }
}
// FILE: main.kt
fun main(a: A) {
a.foo("").length
a.foo(null)?.length
a.bar("").length
a.bar(null)?.length
a.baz1().get(0).length
a.baz1()!!.get(0).length
a.baz1()!!.get(0)?.length
a.baz2().get(0).length
a.baz2()!!.get(0).length
a.baz2()!!.get(0)?.length
a.baz3().get(0).length
a.baz3()!!.get(0).length
a.baz3()!!.get(0)?.length
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
@@ -1,67 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
// JSR305_GLOBAL_REPORT: strict
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NonNullApi {
}
// FILE: spr/NullableApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
@Target({ElementType.TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NullableApi {
}
// FILE: A.java
import spr.*;
import java.util.*;
@NonNullApi
public class A {
public String foo(String x) { return ""; }
public @NullableApi String bar(@NullableApi String y) { return ""; }
public @NullableApi List<String> baz1() { return null; }
}
// FILE: main.kt
fun main(a: A) {
a.foo("").length
a.foo(null)?.length
a.bar("").length
a.bar(null)?.length
a.baz1().get(0).length
a.baz1()!!.get(0).length
a.baz1()!!.get(0)?.length
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
@@ -1,79 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// JSR305_GLOBAL_REPORT: strict
// FILE: spr/Nullable.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierNickname;
import javax.annotation.meta.When;
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierNickname
public @interface Nullable {
}
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NonNullApi {
}
// FILE: A.java
import spr.*;
@NonNullApi
public class A {
public String field = null;
public String foo(String x, @Nullable CharSequence y) {
return "";
}
public String bar() {
return "";
}
@Nullable
public java.util.List<String> baz() {
return null;
}
}
// FILE: main.kt
fun main(a: A) {
a.foo("", null)?.length
a.foo("", null).length
a.foo(null, "").length
a.bar().length
a.bar()!!.length
a.field?.length
a.field.length
a.baz()<!UNSAFE_CALL!>.<!>get(0)
a.baz()!!.get(0).get(0)
a.baz()!!.get(0)?.get(0)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// JSR305_GLOBAL_REPORT: strict
@@ -78,15 +78,15 @@ import test.L
fun main(a: test.A, l: L<Map<String, Int>, Int?>, l1: L<Map<String, Int>, Int>) {
a.foo(l)
a.foo(<!ARGUMENT_TYPE_MISMATCH!>l <!UNCHECKED_CAST!>as L<Map<String, Int>, Int><!><!>)
a.foo(l <!UNCHECKED_CAST!>as L<Map<String, Int?>, Int?><!>)
a.foo(<!ARGUMENT_TYPE_MISMATCH!>l <!UNCHECKED_CAST!>as L<Map<String, Int?>, Int?><!><!>)
a.bar(l1)
a.bar(l1 <!UNCHECKED_CAST!>as L<Map<String, Int>, Int?><!>)
a.bar(<!ARGUMENT_TYPE_MISMATCH!>l1 <!UNCHECKED_CAST!>as L<Map<String, Int>, Int?><!><!>)
a.baz1().t().containsKey("")
a.baz1().t().containsKey(null)
a.baz1().t().<!TYPE_INFERENCE_ONLY_INPUT_TYPES_ERROR!>containsKey<!>(null)
a.baz1().t().containsValue(1)
a.baz1().t().containsValue(null)
a.baz1().t().<!TYPE_INFERENCE_ONLY_INPUT_TYPES_ERROR!>containsValue<!>(null)
a.baz1().s().hashCode()
a.baz1().setT(l.t())
@@ -100,8 +100,8 @@ fun main(a: test.A, l: L<Map<String, Int>, Int?>, l1: L<Map<String, Int>, Int>)
a.baz2().s().hashCode()
a.baz3().t().containsKey("")
a.baz3().t().containsKey(null)
a.baz3().t().<!TYPE_INFERENCE_ONLY_INPUT_TYPES_ERROR!>containsKey<!>(null)
a.baz3().t().containsValue(1)
a.baz3().t().containsValue(null)
a.baz3().t().<!TYPE_INFERENCE_ONLY_INPUT_TYPES_ERROR!>containsValue<!>(null)
a.baz3().s().hashCode()
}
@@ -1,77 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
// JSR305_GLOBAL_REPORT: strict
// FILE: spr/NonNullApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
@Target({ElementType.TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.TYPE_USE, ElementType.METHOD})
public @interface NonNullApi {
}
// FILE: spr/NullableApi.java
package spr;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.TYPE_USE})
public @interface NullableApi {
}
// FILE: A.java
import spr.*;
import java.util.*;
@NonNullApi
public class A {
public String foo(String x) { return ""; }
public @NullableApi String bar(@NullableApi String y) { return ""; }
public @NullableApi List<String> baz1() { return null; }
public List<@NullableApi String> baz2() { return null; }
public @NullableApi List<@NonNullApi String> baz3() { return null; }
}
// FILE: main.kt
fun main(a: A) {
a.foo("").length
a.foo(null)<!UNNECESSARY_SAFE_CALL!>?.<!>length
a.bar("").length
a.bar(null)<!UNNECESSARY_SAFE_CALL!>?.<!>length
a.baz1().get(0).length
a.baz1()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0).length
a.baz1()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0)?.length
a.baz2().get(0).length
a.baz2()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0).length
a.baz2()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0)?.length
a.baz3().get(0).length
a.baz3()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0).length
a.baz3()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.get(0)?.length
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// SKIP_JAVAC
// SOURCE_RETENTION_ANNOTATIONS
@@ -1,56 +0,0 @@
// FULL_JDK
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// ISSUE: KT-56656
// FILE: ParametricNullness.java
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({FIELD, METHOD, PARAMETER})
@javax.annotation.meta.TypeQualifierNickname
@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN)
@interface ParametricNullness {}
// FILE: ElementTypesAreNonnullByDefault.java
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
/**
* Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this
* unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to
* "undo" it as best we can.
*/
@Retention(RUNTIME)
@Target(TYPE)
@TypeQualifierDefault({FIELD, METHOD, PARAMETER})
@Nonnull
@interface ElementTypesAreNonnullByDefault {}
// FILE: MyFunction.java
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ElementTypesAreNonnullByDefault
public interface MyFunction<F extends @Nullable Object, T extends @Nullable Object> extends java.util.function.Function<F, T> {
@Override
@ParametricNullness
T apply (@ParametricNullness F input);
}
// FILE: main.kt
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class A<!> : MyFunction<String?, String?> {
<!NOTHING_TO_OVERRIDE!>override<!> fun apply(x: String?): String? = ""
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// FULL_JDK
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
// ISSUE: KT-56656