mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
[kotlin] K2 J2K: Initial generic nullability inference
TODOs are listed in the J2KNullityInferrer Javadoc. KTIJ-29147 GitOrigin-RevId: 2949b6d7c6b72b586ea6d3af89d0f2e655edcc6d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
64e631f7c9
commit
713072b892
@@ -5196,6 +5196,11 @@ public abstract class NewJavaToKotlinConverterSingleFileTestGenerated extends Ab
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentForNotNullRecordParameter.java")
|
||||
public void testArgumentForNotNullRecordParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullability/argumentForNotNullRecordParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccess.java")
|
||||
public void testArrayAccess() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullability/arrayAccess.java");
|
||||
@@ -5470,15 +5475,140 @@ public abstract class NewJavaToKotlinConverterSingleFileTestGenerated extends Ab
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentForNotNullRecordParameter.java")
|
||||
public void testArgumentForNotNullRecordParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/argumentForNotNullRecordParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("array.java")
|
||||
public void testArray() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/array.java");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayNewNoInitializerTodo.java")
|
||||
public void testArrayNewNoInitializerTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/arrayNewNoInitializerTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("assignment.java")
|
||||
public void testAssignment() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/assignment.java");
|
||||
}
|
||||
|
||||
@TestMetadata("complex.java")
|
||||
public void testComplex() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/complex.java");
|
||||
}
|
||||
|
||||
@TestMetadata("javaInteropTodo.java")
|
||||
public void testJavaInteropTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/javaInteropTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinInteropTodo.java")
|
||||
public void testKotlinInteropTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/kotlinInteropTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("libraryMethodsTodo.java")
|
||||
public void testLibraryMethodsTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/libraryMethodsTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("methodCallArgument.java")
|
||||
public void testMethodCallArgument() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/methodCallArgument.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterArray.java")
|
||||
public void testNotNullIterationParameterArray() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterArray.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterCollections.java")
|
||||
public void testNotNullIterationParameterCollections() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterCollections.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterMethodCall.java")
|
||||
public void testNotNullIterationParameterMethodCall() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterMethodCall.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullParameterAsArgument.java")
|
||||
public void testNotNullParameterAsArgument() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullParameterAsArgument.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullTypeArgumentFromKotlinTypeParameterInCall.java")
|
||||
public void testNotNullTypeArgumentFromKotlinTypeParameterInCall() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullTypeArgumentFromKotlinTypeParameterInCall.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullTypeArgumentFromKotlinTypeParameterInDeclaration.java")
|
||||
public void testNotNullTypeArgumentFromKotlinTypeParameterInDeclaration() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullTypeArgumentFromKotlinTypeParameterInDeclaration.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nullabilityAnnotations.java")
|
||||
public void testNullabilityAnnotations() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/nullabilityAnnotations.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromArrayParameter.java")
|
||||
public void testPropagateFromArrayParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromArrayParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromConstructorParameter.java")
|
||||
public void testPropagateFromConstructorParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromConstructorParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromField.java")
|
||||
public void testPropagateFromField() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromField.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromMethodReturnType.java")
|
||||
public void testPropagateFromMethodReturnType() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromMethodReturnType.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromParameterToField.java")
|
||||
public void testPropagateFromParameterToField() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromParameterToField.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromVariableRecursive.java")
|
||||
public void testPropagateFromVariableRecursive() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromVariableRecursive.java");
|
||||
}
|
||||
|
||||
@TestMetadata("returnMethodCallExprTodo.java")
|
||||
public void testReturnMethodCallExprTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/returnMethodCallExprTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.java")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/simple.java");
|
||||
}
|
||||
|
||||
@TestMetadata("typeArgumentTodo.java")
|
||||
public void testTypeArgumentTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/typeArgumentTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterNotNullUpperBound.java")
|
||||
public void testTypeParameterNotNullUpperBound() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/typeParameterNotNullUpperBound.java");
|
||||
}
|
||||
|
||||
@TestMetadata("wildcard.java")
|
||||
public void testWildcard() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/wildcard.java");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -5196,6 +5196,11 @@ public abstract class K2JavaToKotlinConverterSingleFileTestGenerated extends Abs
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentForNotNullRecordParameter.java")
|
||||
public void testArgumentForNotNullRecordParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullability/argumentForNotNullRecordParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccess.java")
|
||||
public void testArrayAccess() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullability/arrayAccess.java");
|
||||
@@ -5470,15 +5475,140 @@ public abstract class K2JavaToKotlinConverterSingleFileTestGenerated extends Abs
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentForNotNullRecordParameter.java")
|
||||
public void testArgumentForNotNullRecordParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/argumentForNotNullRecordParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("array.java")
|
||||
public void testArray() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/array.java");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayNewNoInitializerTodo.java")
|
||||
public void testArrayNewNoInitializerTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/arrayNewNoInitializerTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("assignment.java")
|
||||
public void testAssignment() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/assignment.java");
|
||||
}
|
||||
|
||||
@TestMetadata("complex.java")
|
||||
public void testComplex() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/complex.java");
|
||||
}
|
||||
|
||||
@TestMetadata("javaInteropTodo.java")
|
||||
public void testJavaInteropTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/javaInteropTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinInteropTodo.java")
|
||||
public void testKotlinInteropTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/kotlinInteropTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("libraryMethodsTodo.java")
|
||||
public void testLibraryMethodsTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/libraryMethodsTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("methodCallArgument.java")
|
||||
public void testMethodCallArgument() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/methodCallArgument.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterArray.java")
|
||||
public void testNotNullIterationParameterArray() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterArray.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterCollections.java")
|
||||
public void testNotNullIterationParameterCollections() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterCollections.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullIterationParameterMethodCall.java")
|
||||
public void testNotNullIterationParameterMethodCall() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullIterationParameterMethodCall.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullParameterAsArgument.java")
|
||||
public void testNotNullParameterAsArgument() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullParameterAsArgument.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullTypeArgumentFromKotlinTypeParameterInCall.java")
|
||||
public void testNotNullTypeArgumentFromKotlinTypeParameterInCall() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullTypeArgumentFromKotlinTypeParameterInCall.java");
|
||||
}
|
||||
|
||||
@TestMetadata("notNullTypeArgumentFromKotlinTypeParameterInDeclaration.java")
|
||||
public void testNotNullTypeArgumentFromKotlinTypeParameterInDeclaration() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/notNullTypeArgumentFromKotlinTypeParameterInDeclaration.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nullabilityAnnotations.java")
|
||||
public void testNullabilityAnnotations() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/nullabilityAnnotations.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromArrayParameter.java")
|
||||
public void testPropagateFromArrayParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromArrayParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromConstructorParameter.java")
|
||||
public void testPropagateFromConstructorParameter() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromConstructorParameter.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromField.java")
|
||||
public void testPropagateFromField() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromField.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromMethodReturnType.java")
|
||||
public void testPropagateFromMethodReturnType() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromMethodReturnType.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromParameterToField.java")
|
||||
public void testPropagateFromParameterToField() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromParameterToField.java");
|
||||
}
|
||||
|
||||
@TestMetadata("propagateFromVariableRecursive.java")
|
||||
public void testPropagateFromVariableRecursive() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/propagateFromVariableRecursive.java");
|
||||
}
|
||||
|
||||
@TestMetadata("returnMethodCallExprTodo.java")
|
||||
public void testReturnMethodCallExprTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/returnMethodCallExprTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.java")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/simple.java");
|
||||
}
|
||||
|
||||
@TestMetadata("typeArgumentTodo.java")
|
||||
public void testTypeArgumentTodo() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/typeArgumentTodo.java");
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterNotNullUpperBound.java")
|
||||
public void testTypeParameterNotNullUpperBound() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/typeParameterNotNullUpperBound.java");
|
||||
}
|
||||
|
||||
@TestMetadata("wildcard.java")
|
||||
public void testWildcard() throws Exception {
|
||||
runTest("../../shared/tests/testData/newJ2k/nullabilityGenerics/wildcard.java");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -2,7 +2,8 @@ progress.text={0} - phase {1,number,#} of {2,number,#}
|
||||
subphase.progress.text={0} ({1,number,#}/{2,number,#}) - phase {3,number,#} of {4,number,#}
|
||||
progress.searching.usages.to.update=Searching usages to update\u2026
|
||||
|
||||
phase.converting.j2k=Converting Java code to Kotlin code
|
||||
j2k.phase.preprocessing=Preprocessing Java files
|
||||
j2k.phase.converting=Converting Java code to Kotlin code
|
||||
j2k.applying.conversions=Applying conversions\u2026
|
||||
|
||||
j2k.custom.preprocessing=Running custom preprocessors
|
||||
|
||||
@@ -4,12 +4,14 @@ package org.jetbrains.kotlin.nj2k;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.NullabilityAnnotationInfo;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaNullability;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil;
|
||||
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
|
||||
import com.intellij.codeInspection.dataFlow.inference.JavaSourceInference;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightRecordCanonicalConstructor;
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
@@ -17,8 +19,8 @@ import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Query;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -26,10 +28,22 @@ import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.nj2k.NullabilityUtilsKt.*;
|
||||
|
||||
/**
|
||||
* This is a copy of com.intellij.codeInspection.inferNullity.NullityInferrer
|
||||
* with some modifications to better support J2K. These modifications may be later
|
||||
* ported back, and this copy may be deleted.
|
||||
* This class is based on com.intellij.codeInspection.inferNullity.NullityInferrer
|
||||
* and extended for better J2K support.
|
||||
* TODO:
|
||||
* support inference for type arguments in case the type parameter is used as both an input and an output type (Collections.emptyList())
|
||||
* support method calls of external methods (especially Kotlin interop)
|
||||
* improve support for array types and initializers
|
||||
* support propagation of generic nullability from lambda parameters
|
||||
* take overridden signatures into account when inferring parameter nullability
|
||||
* support collections mutability inference
|
||||
* try to migrate from direct usage of PsiType's
|
||||
* additional profiling/optimization
|
||||
* document how it works
|
||||
* convert to Kotlin
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
class J2KNullityInferrer {
|
||||
@@ -43,15 +57,34 @@ class J2KNullityInferrer {
|
||||
// because a PsiType object can sometimes be recreated (for example, by PsiNewExpressionImpl.getType()),
|
||||
// and we won't find it in our IdentityHashMap.
|
||||
// We should try to migrate this logic to PsiTypeElements, which are more persistent.
|
||||
private final Set<PsiType> myNotNullTypes = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private final Set<PsiType> myNullableTypes = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private final Set<PsiType> notNullTypes = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private final Set<PsiType> nullableTypes = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
|
||||
// Some types point to a PsiJavaCodeReferenceElement, in this case we mark them as well.
|
||||
// This helps to avoid the problem with recreated types (described above),
|
||||
// because two different types may point to the same PsiJavaCodeReferenceElement.
|
||||
private final Set<PsiJavaCodeReferenceElement> notNullElements = new HashSet<>();
|
||||
private final Set<PsiJavaCodeReferenceElement> nullableElements = new HashSet<>();
|
||||
|
||||
// Caches
|
||||
private final Map<PsiVariable, Collection<PsiReference>> variableReferences = new HashMap<>();
|
||||
private final Map<PsiVariable, Collection<PsiExpression>> variableAssignmentRightHandSides = new HashMap<>();
|
||||
private final Map<PsiParameter, List<PsiReferenceExpression>> parameterReferences = new HashMap<>();
|
||||
|
||||
Set<PsiType> getNotNullTypes() {
|
||||
return myNotNullTypes;
|
||||
return notNullTypes;
|
||||
}
|
||||
|
||||
Set<PsiType> getNullableTypes() {
|
||||
return myNullableTypes;
|
||||
return nullableTypes;
|
||||
}
|
||||
|
||||
Set<PsiJavaCodeReferenceElement> getNotNullElements() {
|
||||
return notNullElements;
|
||||
}
|
||||
|
||||
Set<PsiJavaCodeReferenceElement> getNullableElements() {
|
||||
return nullableElements;
|
||||
}
|
||||
|
||||
private boolean expressionIsNeverNull(@Nullable PsiExpression expression) {
|
||||
@@ -88,12 +121,7 @@ class J2KNullityInferrer {
|
||||
return false;
|
||||
}
|
||||
|
||||
SearchScope scope = getScope(variable);
|
||||
if (scope == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Query<PsiReference> references = ReferencesSearch.search(variable, scope);
|
||||
final Collection<PsiReference> references = variableReferences.get(variable);
|
||||
for (final PsiReference reference : references) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (!(element instanceof PsiReferenceExpression)) {
|
||||
@@ -124,7 +152,7 @@ class J2KNullityInferrer {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Query<PsiReference> references = ReferencesSearch.search(variable, scope);
|
||||
final Collection<PsiReference> references = variableReferences.get(variable);
|
||||
for (final PsiReference reference : references) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (!(element instanceof PsiReferenceExpression)) {
|
||||
@@ -165,7 +193,14 @@ class J2KNullityInferrer {
|
||||
}
|
||||
|
||||
public void collect(@NotNull PsiFile file) {
|
||||
// Step 1: mark all types with known nullability inferred from Java DFA
|
||||
runPreprocessing(file);
|
||||
inferNullabilityIteratively(file);
|
||||
flushCaches();
|
||||
}
|
||||
|
||||
// Mark all types with known nullability inferred from Java DFA
|
||||
// and cache some variable reference searches
|
||||
private void runPreprocessing(@NotNull PsiFile file) {
|
||||
file.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitTypeElement(@NotNull PsiTypeElement typeElement) {
|
||||
@@ -177,9 +212,41 @@ class J2KNullityInferrer {
|
||||
case NOT_NULL -> registerNotNullType(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Step 2: infer nullability for the rest of types
|
||||
@Override
|
||||
public void visitVariable(@NotNull PsiVariable variable) {
|
||||
super.visitVariable(variable);
|
||||
if (variable.getType() instanceof PsiPrimitiveType) return;
|
||||
|
||||
SearchScope scope = getScope(variable);
|
||||
Collection<PsiReference> references = Collections.emptyList();
|
||||
if (scope != null) {
|
||||
references = ReferencesSearch.search(variable, scope).findAll();
|
||||
}
|
||||
variableReferences.put(variable, references);
|
||||
|
||||
Collection<PsiExpression> rightHandSides = DfaPsiUtil.getVariableAssignmentsInFile(variable, false, null);
|
||||
variableAssignmentRightHandSides.put(variable, rightHandSides);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(@NotNull PsiParameter parameter) {
|
||||
super.visitParameter(parameter);
|
||||
if (parameter.getType() instanceof PsiPrimitiveType) return;
|
||||
|
||||
List<PsiReferenceExpression> references = Collections.emptyList();
|
||||
final PsiElement grandParent = parameter.getDeclarationScope();
|
||||
if (grandParent instanceof PsiMethod method) {
|
||||
if (method.getBody() != null) {
|
||||
references = VariableAccessUtils.getVariableReferences(parameter, method);
|
||||
}
|
||||
}
|
||||
parameterReferences.put(parameter, references);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void inferNullabilityIteratively(@NotNull PsiFile file) {
|
||||
int prevNumAnnotationsAdded;
|
||||
int pass = 0;
|
||||
do {
|
||||
@@ -191,6 +258,12 @@ class J2KNullityInferrer {
|
||||
while (prevNumAnnotationsAdded < numAnnotationsAdded && pass < MAX_PASSES);
|
||||
}
|
||||
|
||||
private void flushCaches() {
|
||||
variableReferences.clear();
|
||||
variableAssignmentRightHandSides.clear();
|
||||
parameterReferences.clear();
|
||||
}
|
||||
|
||||
private void registerNullableAnnotation(@NotNull PsiModifierListOwner declaration) {
|
||||
registerAnnotation(declaration, true);
|
||||
}
|
||||
@@ -217,12 +290,16 @@ class J2KNullityInferrer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerNullableType(@NotNull PsiType type) {
|
||||
registerTypeNullability(type, true);
|
||||
private void registerNullableType(@Nullable PsiType type) {
|
||||
if (type != null) {
|
||||
registerTypeNullability(type, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerNotNullType(@NotNull PsiType type) {
|
||||
registerTypeNullability(type, false);
|
||||
private void registerNotNullType(@Nullable PsiType type) {
|
||||
if (type != null) {
|
||||
registerTypeNullability(type, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTypeNullability(@NotNull PsiType type, boolean isNullable) {
|
||||
@@ -238,28 +315,42 @@ class J2KNullityInferrer {
|
||||
return;
|
||||
}
|
||||
|
||||
// We conservatively skip type parameter references for now (their nullability is always considered unknown)
|
||||
// TODO support it
|
||||
if (type instanceof PsiClassType classType) {
|
||||
PsiClass psiClass = classType.resolve();
|
||||
if (psiClass instanceof PsiTypeParameter) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PsiJavaCodeReferenceElement element = null;
|
||||
if (type instanceof PsiClassReferenceType classReferenceType) {
|
||||
element = classReferenceType.getReference();
|
||||
}
|
||||
|
||||
PsiType unwrappedType = unwrap(type);
|
||||
|
||||
if (isNullable) {
|
||||
myNullableTypes.add(unwrappedType);
|
||||
myNotNullTypes.remove(unwrappedType);
|
||||
nullableTypes.add(unwrappedType);
|
||||
if (element != null) nullableElements.add(element);
|
||||
|
||||
notNullTypes.remove(unwrappedType);
|
||||
if (element != null) notNullElements.remove(element);
|
||||
} else {
|
||||
myNotNullTypes.add(unwrappedType);
|
||||
notNullTypes.add(unwrappedType);
|
||||
if (element != null) notNullElements.add(element);
|
||||
}
|
||||
|
||||
numAnnotationsAdded++;
|
||||
}
|
||||
|
||||
private boolean registerAnnotationByNullAssignmentStatus(PsiVariable variable) {
|
||||
private void registerAnnotationByNullAssignmentStatus(PsiVariable variable) {
|
||||
if (variableNeverAssignedNull(variable)) {
|
||||
registerNotNullAnnotation(variable);
|
||||
return true;
|
||||
} else if (variableSometimesAssignedNull(variable)) {
|
||||
registerNullableAnnotation(variable);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isNotNull(@Nullable PsiModifierListOwner owner) {
|
||||
@@ -272,9 +363,10 @@ class J2KNullityInferrer {
|
||||
return isNotNull(type);
|
||||
}
|
||||
|
||||
private boolean isNotNull(@NotNull PsiType type) {
|
||||
private boolean isNotNull(@Nullable PsiType type) {
|
||||
if (type == null) return false;
|
||||
PsiType unwrappedType = unwrap(type);
|
||||
return myNotNullTypes.contains(unwrappedType);
|
||||
return notNullTypes.contains(unwrappedType);
|
||||
}
|
||||
|
||||
private boolean isNullable(@Nullable PsiModifierListOwner owner) {
|
||||
@@ -287,9 +379,10 @@ class J2KNullityInferrer {
|
||||
return isNullable(type);
|
||||
}
|
||||
|
||||
private boolean isNullable(@NotNull PsiType type) {
|
||||
private boolean isNullable(@Nullable PsiType type) {
|
||||
if (type == null) return false;
|
||||
PsiType unwrappedType = unwrap(type);
|
||||
return myNullableTypes.contains(unwrappedType);
|
||||
return nullableTypes.contains(unwrappedType);
|
||||
}
|
||||
|
||||
private static @NotNull PsiType unwrap(@NotNull PsiType type) {
|
||||
@@ -302,19 +395,18 @@ class J2KNullityInferrer {
|
||||
return type;
|
||||
}
|
||||
|
||||
private boolean hasNullability(@NotNull PsiModifierListOwner owner) {
|
||||
private boolean hasRawNullability(@NotNull PsiType type) {
|
||||
return isNullable(type) || isNotNull(type);
|
||||
}
|
||||
|
||||
private boolean hasRawNullability(@NotNull PsiModifierListOwner owner) {
|
||||
NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject());
|
||||
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(owner);
|
||||
if (info != null && !info.isInferred() && info.getNullability() != Nullability.UNKNOWN) return true;
|
||||
|
||||
PsiType type = getType(owner);
|
||||
if (type == null) return false;
|
||||
|
||||
return hasNullability(type);
|
||||
}
|
||||
|
||||
private boolean hasNullability(@NotNull PsiType type) {
|
||||
return isNullable(type) || isNotNull(type);
|
||||
return hasRawNullability(type);
|
||||
}
|
||||
|
||||
private class NullityInferrerVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
@@ -326,6 +418,9 @@ class J2KNullityInferrer {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO deal with overrides somehow
|
||||
unifyNullabilityOfMethodReturnTypeAndReturnedExpressions(method);
|
||||
|
||||
final Collection<PsiMethod> overridingMethods = OverridingMethodsSearch.search(method).findAll();
|
||||
for (final PsiMethod overridingMethod : overridingMethods) {
|
||||
if (isNullable(overridingMethod)) {
|
||||
@@ -340,11 +435,11 @@ class J2KNullityInferrer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasNullability(method)) {
|
||||
if (hasRawNullability(method)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Nullability nullability = DfaUtil.inferMethodNullability(method);
|
||||
Nullability nullability = getMethodNullabilityByDfa(method);
|
||||
switch (nullability) {
|
||||
case NULLABLE -> registerNullableAnnotation(method);
|
||||
|
||||
@@ -359,15 +454,60 @@ class J2KNullityInferrer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
|
||||
super.visitLocalVariable(variable);
|
||||
if (variable.getType() instanceof PsiPrimitiveType || hasNullability(variable)) {
|
||||
private void unifyNullabilityOfMethodReturnTypeAndReturnedExpressions(@NotNull PsiMethod method) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
|
||||
PsiType methodReturnType = method.getReturnType();
|
||||
if (methodReturnType == null) return;
|
||||
|
||||
PsiReturnStatement[] statements = PsiUtil.findReturnStatements(method);
|
||||
if (statements.length == 0) {
|
||||
// Stick to nullable by default for empty methods (ex. in interfaces)
|
||||
return;
|
||||
}
|
||||
|
||||
if (registerAnnotationByNullAssignmentStatus(variable)) return;
|
||||
boolean allRawReturnValueTypesAreNotNull = true;
|
||||
boolean rawMethodReturnTypeIsNotNull = isNotNull(methodReturnType);
|
||||
|
||||
for (PsiReturnStatement statement : statements) {
|
||||
PsiExpression returnValue = statement.getReturnValue();
|
||||
if (returnValue == null) continue;
|
||||
|
||||
PsiType returnValueType = getReferenceType(returnValue);
|
||||
if (returnValueType == null) continue;
|
||||
|
||||
if (rawMethodReturnTypeIsNotNull) {
|
||||
// TODO simplify
|
||||
propagateRawNullabilityWithPsiContext(methodReturnType, returnValueType);
|
||||
propagateRawNullabilityWithPsiContext(methodReturnType, returnValue.getType());
|
||||
}
|
||||
|
||||
if (!isNotNull(returnValueType)) {
|
||||
allRawReturnValueTypesAreNotNull = false;
|
||||
}
|
||||
|
||||
// TODO Looks questionable to update parts of type arguments like this
|
||||
unifyGenericNullability(methodReturnType, returnValueType); // TODO extend to `returnValue.getType()`?
|
||||
}
|
||||
|
||||
if (allRawReturnValueTypesAreNotNull) {
|
||||
registerNotNullType(methodReturnType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
|
||||
super.visitLocalVariable(variable);
|
||||
|
||||
PsiType variableType = variable.getType();
|
||||
if (variableType instanceof PsiPrimitiveType) return;
|
||||
|
||||
if (!hasRawNullability(variableType)) {
|
||||
registerAnnotationByNullAssignmentStatus(variable);
|
||||
}
|
||||
|
||||
inferNullabilityFromVariableReferences(variable);
|
||||
propagateNullabilityFromVariable(variable);
|
||||
}
|
||||
|
||||
private void inferNullabilityFromVariableReferences(@NotNull PsiVariable variable) {
|
||||
@@ -376,10 +516,7 @@ class J2KNullityInferrer {
|
||||
return;
|
||||
}
|
||||
|
||||
SearchScope scope = getScope(variable);
|
||||
if (scope == null) return;
|
||||
|
||||
final Query<PsiReference> references = ReferencesSearch.search(variable, scope);
|
||||
final Collection<PsiReference> references = variableReferences.get(variable);
|
||||
for (final PsiReference reference : references) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (!(element instanceof PsiReferenceExpression referenceExpression)) {
|
||||
@@ -390,19 +527,89 @@ class J2KNullityInferrer {
|
||||
PsiTreeUtil.skipParentsOfType(referenceExpression, PsiParenthesizedExpression.class, PsiTypeCastExpression.class);
|
||||
|
||||
processReference(variable, referenceExpression, refParent);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNullable(variable)) {
|
||||
// We determined that the variable is nullable, so there is no need to check the rest of references.
|
||||
// But if it is not-null, we keep going because we may find that it is nullable later, after all.
|
||||
break;
|
||||
private void propagateNullabilityFromVariable(PsiVariable variable) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
|
||||
PsiType variableType = variable.getType();
|
||||
Collection<PsiExpression> rightHandSides = variableAssignmentRightHandSides.get(variable);
|
||||
for (PsiExpression expr : rightHandSides) {
|
||||
PsiType exprType = expr.getType();
|
||||
PsiType referenceType = getReferenceType(expr);
|
||||
|
||||
if (isNullable(exprType) || isNullable(referenceType)) {
|
||||
registerNullableType(variableType);
|
||||
} else if (isNotNull(variableType)) {
|
||||
propagateRawNullabilityWithPsiContext(variableType, exprType);
|
||||
propagateRawNullabilityWithPsiContext(variableType, referenceType);
|
||||
}
|
||||
|
||||
unifyGenericNullabilityWithPsiContext(variableType, exprType);
|
||||
unifyGenericNullabilityWithPsiContext(variableType, referenceType);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO rethink how to use this properly
|
||||
private static @Nullable PsiType getReferenceType(PsiExpression expression) {
|
||||
if (expression instanceof PsiReferenceExpression referenceExpression) {
|
||||
PsiElement target = referenceExpression.resolve();
|
||||
if (target instanceof PsiVariable variable) {
|
||||
return variable.getType();
|
||||
}
|
||||
} else if (expression instanceof PsiMethodCallExpression methodCallExpression) {
|
||||
PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method != null) {
|
||||
return method.getReturnType();
|
||||
}
|
||||
}
|
||||
return expression.getType();
|
||||
}
|
||||
|
||||
private void unifyGenericNullabilityWithPsiContext(@Nullable PsiType type1, @Nullable PsiType type2) {
|
||||
propagateGenericNullabilityWithPsiContext(type1, type2);
|
||||
propagateGenericNullabilityWithPsiContext(type2, type1);
|
||||
}
|
||||
|
||||
private void propagateGenericNullabilityWithPsiContext(
|
||||
@Nullable PsiType originType,
|
||||
@Nullable PsiType targetType
|
||||
) {
|
||||
if (originType == null || targetType == null) return;
|
||||
|
||||
int prevCount = numAnnotationsAdded;
|
||||
propagateGenericNullability(originType, targetType, false);
|
||||
if (prevCount == numAnnotationsAdded) {
|
||||
// Nullability is already the same, we can stop here
|
||||
return;
|
||||
}
|
||||
|
||||
PsiType psiContextType = getPsiContextType(targetType);
|
||||
if (psiContextType != null) {
|
||||
propagateGenericNullabilityWithPsiContext(originType, psiContextType);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable PsiType getPsiContextType(@NotNull PsiType type) { // TODO it is not a chain!
|
||||
if (!(type instanceof PsiClassType classType)) return null;
|
||||
|
||||
PsiElement psiContext = classType.getPsiContext();
|
||||
if (!(psiContext instanceof PsiJavaCodeReferenceElement javaCodeReferenceElement)) return null;
|
||||
|
||||
PsiElement codeReferenceElementParent = javaCodeReferenceElement.getParent();
|
||||
if (!(codeReferenceElementParent instanceof PsiTypeElement typeElement)) return null;
|
||||
|
||||
PsiType psiContextType = typeElement.getType();
|
||||
if (type == psiContextType) return null;
|
||||
|
||||
return psiContextType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(@NotNull PsiParameter parameter) {
|
||||
super.visitParameter(parameter);
|
||||
if (parameter.getType() instanceof PsiPrimitiveType || hasNullability(parameter)) {
|
||||
if (parameter.getType() instanceof PsiPrimitiveType) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -411,13 +618,15 @@ class J2KNullityInferrer {
|
||||
if (method.getBody() != null) {
|
||||
if (JavaSourceInference.inferNullability(parameter) == Nullability.NOT_NULL) {
|
||||
registerNotNullAnnotation(parameter);
|
||||
return;
|
||||
}
|
||||
|
||||
for (PsiReferenceExpression expr : VariableAccessUtils.getVariableReferences(parameter, method)) {
|
||||
List<PsiReferenceExpression> references = parameterReferences.get(parameter);
|
||||
for (PsiReferenceExpression expr : references) {
|
||||
final PsiElement parent =
|
||||
PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class, PsiTypeCastExpression.class);
|
||||
if (processReference(parameter, expr, parent)) return;
|
||||
|
||||
processReference(parameter, expr, parent);
|
||||
|
||||
if (isNotNull(method)) {
|
||||
PsiElement toReturn = parent;
|
||||
if (parent instanceof PsiConditionalExpression &&
|
||||
@@ -426,18 +635,19 @@ class J2KNullityInferrer {
|
||||
}
|
||||
if (toReturn instanceof PsiReturnStatement) {
|
||||
registerNotNullAnnotation(parameter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (grandParent instanceof PsiForeachStatement) {
|
||||
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(grandParent))) {
|
||||
} else if (grandParent instanceof PsiForeachStatement foreachStatement) {
|
||||
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(foreachStatement))) {
|
||||
final PsiElement place = reference.getElement();
|
||||
if (place instanceof PsiReferenceExpression expr) {
|
||||
final PsiElement parent =
|
||||
PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class, PsiTypeCastExpression.class);
|
||||
if (processReference(parameter, expr, parent)) return;
|
||||
if (processReference(parameter, expr, parent)) {
|
||||
propagateRawNullabilityToIterableComponentType(foreachStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -445,18 +655,53 @@ class J2KNullityInferrer {
|
||||
}
|
||||
}
|
||||
|
||||
private void propagateRawNullabilityToIterableComponentType(@NotNull PsiForeachStatement foreachStatement) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
|
||||
PsiType parameterType = foreachStatement.getIterationParameter().getType();
|
||||
if (!isNullable(parameterType) && !isNotNull(parameterType)) return;
|
||||
|
||||
PsiExpression iteratedValue = foreachStatement.getIteratedValue();
|
||||
if (iteratedValue == null) return;
|
||||
|
||||
PsiType componentType = JavaGenericsUtil.getCollectionItemType(iteratedValue);
|
||||
if (componentType == null) return;
|
||||
|
||||
propagateRawNullabilityWithPsiContext(parameterType, componentType);
|
||||
}
|
||||
|
||||
private void propagateRawNullabilityWithPsiContext(@Nullable PsiType originType, @Nullable PsiType targetType) {
|
||||
if (originType == null || targetType == null) return;
|
||||
if (isNullable(targetType) || isNotNull(targetType)) return; // TODO is this too restrictive check?
|
||||
|
||||
if (isNullable(originType)) {
|
||||
registerNullableType(targetType);
|
||||
} else if (isNotNull(originType)) {
|
||||
registerNotNullType(targetType);
|
||||
}
|
||||
|
||||
PsiType psiContextType = getPsiContextType(targetType);
|
||||
if (psiContextType != null) {
|
||||
propagateRawNullabilityWithPsiContext(originType, psiContextType);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processReference(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expr, PsiElement parent) {
|
||||
if (NullabilityUtilsKt.isUsedInAutoUnboxingContext(expr)) {
|
||||
if (isUsedInAutoUnboxingContext(expr)) {
|
||||
registerNotNullAnnotation(variable);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PsiUtil.isAccessedForWriting(expr)) return true;
|
||||
|
||||
if (NullabilityUtilsKt.getDfaNullability(expr) == DfaNullability.NOT_NULL) {
|
||||
if (getExpressionDfaNullability(expr) == DfaNullability.NOT_NULL) {
|
||||
// If Java DFA can determine that the nullability of the reference is definitely not-null,
|
||||
// we are probably inside an "instance of" check (which is like a smart cast for Java DFA).
|
||||
// So we give up trying to guess the nullability of the declaration from this reference.
|
||||
//
|
||||
// However, try to process an argument reference to propagate generic nullability,
|
||||
// regardless of top-level nullability inferred from DFA,
|
||||
// since it is orthogonal to the nullability of type arguments.
|
||||
processArgumentReference(variable, expr, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -520,14 +765,18 @@ class J2KNullityInferrer {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (processArgumentReference(variable, expr)) {
|
||||
if (processArgumentReference(variable, expr, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processArgumentReference(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expr) {
|
||||
private boolean processArgumentReference(
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull PsiReferenceExpression expr,
|
||||
boolean updateRawNullability
|
||||
) {
|
||||
final PsiCall call = PsiTreeUtil.getParentOfType(expr, PsiCall.class);
|
||||
if (call == null) return false;
|
||||
final PsiExpressionList argumentList = call.getArgumentList();
|
||||
@@ -544,11 +793,21 @@ class J2KNullityInferrer {
|
||||
//not vararg
|
||||
if (idx >= parameters.length) return false;
|
||||
|
||||
final PsiParameter resolvedToParam = parameters[idx];
|
||||
boolean isArray = variable.getType() instanceof PsiArrayType;
|
||||
boolean isVarArgs = variable instanceof PsiParameter parameter && parameter.isVarArgs();
|
||||
PsiVariable resolvedToParam = parameters[idx];
|
||||
if (resolvedToParam instanceof LightRecordCanonicalConstructor.LightRecordConstructorParameter) {
|
||||
resolvedToParam = ((LightRecordCanonicalConstructor.LightRecordConstructorParameter) resolvedToParam).getRecordComponent();
|
||||
}
|
||||
if (resolvedToParam == null) return false;
|
||||
|
||||
if (isNotNull(resolvedToParam) || (isArray && !isVarArgs && resolvedToParam.isVarArgs())) {
|
||||
PsiType variableType = variable.getType();
|
||||
PsiType parameterType = resolvedToParam.getType();
|
||||
|
||||
unifyGenericNullability(variableType, parameterType);
|
||||
|
||||
if (!updateRawNullability) return false;
|
||||
|
||||
if (isNotNull(resolvedToParam) ||
|
||||
(variableType instanceof PsiArrayType && !isVarArgs(variable) && isVarArgs(resolvedToParam))) {
|
||||
// In the case of varargs in Kotlin, the spread operator needs to be applied to a not-null array
|
||||
registerNotNullAnnotation(variable);
|
||||
return true;
|
||||
@@ -557,16 +816,160 @@ class J2KNullityInferrer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isVarArgs(PsiVariable variable) {
|
||||
if (variable instanceof PsiParameter) {
|
||||
return ((PsiParameter) variable).isVarArgs();
|
||||
} else if (variable instanceof PsiRecordComponent) {
|
||||
return ((PsiRecordComponent) variable).isVarArgs();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unifyGenericNullability(PsiType type1, PsiType type2) {
|
||||
propagateGenericNullability(type1, type2, false);
|
||||
propagateGenericNullability(type2, type1, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates nullability of the target type and its type arguments recursively from the origin type.
|
||||
*/
|
||||
private void propagateGenericNullability(PsiType originType, PsiType targetType, boolean updateRawType) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
|
||||
if (updateRawType) {
|
||||
if (isNotNull(originType)) {
|
||||
registerNotNullType(targetType);
|
||||
} else if (isNullable(originType)) {
|
||||
registerNullableType(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
if (originType instanceof PsiArrayType originArrayType && targetType instanceof PsiArrayType targetArrayType) {
|
||||
PsiType originComponentType = originArrayType.getComponentType();
|
||||
PsiType targetComponentType = targetArrayType.getComponentType();
|
||||
propagateGenericNullability(originComponentType, targetComponentType, true);
|
||||
}
|
||||
|
||||
if (!(originType instanceof PsiClassType originClassType)) return;
|
||||
if (!(targetType instanceof PsiClassType targetClassType)) return;
|
||||
|
||||
PsiType[] originTypeArguments = originClassType.getParameters();
|
||||
PsiType[] targetTypeArguments = targetClassType.getParameters();
|
||||
if (originTypeArguments.length != targetTypeArguments.length) return;
|
||||
|
||||
for (int i = 0; i < originTypeArguments.length; i++) {
|
||||
PsiType originTypeArgument = originTypeArguments[i];
|
||||
PsiType targetTypeArgument = targetTypeArguments[i];
|
||||
propagateGenericNullability(originTypeArgument, targetTypeArgument, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(@NotNull PsiField field) {
|
||||
super.visitField(field);
|
||||
if (field instanceof PsiEnumConstant || field.getType() instanceof PsiPrimitiveType || hasNullability(field)) {
|
||||
return;
|
||||
if (field instanceof PsiEnumConstant) return;
|
||||
|
||||
PsiType fieldType = field.getType();
|
||||
if (fieldType instanceof PsiPrimitiveType) return;
|
||||
|
||||
if (!hasRawNullability(fieldType)) {
|
||||
registerAnnotationByNullAssignmentStatus(field);
|
||||
}
|
||||
|
||||
if (registerAnnotationByNullAssignmentStatus(field)) return;
|
||||
if (!isPrivate(field)) return;
|
||||
inferNullabilityFromVariableReferences(field);
|
||||
propagateNullabilityFromVariable(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
|
||||
// Update the nullability of type arguments in constructor calls
|
||||
PsiElement target = reference.resolve();
|
||||
if (!(target instanceof PsiClass klass)) return;
|
||||
|
||||
PsiTypeParameter[] typeParameters = klass.getTypeParameters();
|
||||
PsiType[] typeArguments = reference.getTypeParameters();
|
||||
|
||||
updateNullabilityOfTypeArguments(typeParameters, typeArguments);
|
||||
}
|
||||
|
||||
// If type parameters come from Kotlin, we propagate their nullability to the type arguments
|
||||
// TODO support not only raw but generic propagation
|
||||
private void updateNullabilityOfTypeArguments(PsiTypeParameter[] typeParameters, PsiType[] typeArguments) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
if (typeParameters.length != typeArguments.length) return;
|
||||
|
||||
for (int i = 0; i < typeParameters.length; i++) {
|
||||
PsiTypeParameter typeParameter = typeParameters[i];
|
||||
PsiType typeArgument = typeArguments[i];
|
||||
Nullability nullability = getTypeParameterNullability(typeParameter);
|
||||
|
||||
switch (nullability) {
|
||||
case NULLABLE -> registerNullableType(typeArgument);
|
||||
case NOT_NULL -> registerNotNullType(typeArgument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
super.visitMethodCallExpression(expression);
|
||||
|
||||
PsiMethod method = expression.resolveMethod();
|
||||
if (method == null) return;
|
||||
|
||||
// Unify generic nullability of parameter-argument pairs
|
||||
PsiExpression[] argumentList = expression.getArgumentList().getExpressions();
|
||||
for (PsiExpression argument : argumentList) {
|
||||
PsiParameter parameter = MethodCallUtils.getParameterForArgument(argument);
|
||||
if (parameter == null) continue;
|
||||
PsiType parameterType = parameter.getType();
|
||||
PsiType argumentType = getReferenceType(argument);
|
||||
unifyGenericNullability(parameterType, argumentType);
|
||||
}
|
||||
|
||||
// Update nullability of type arguments
|
||||
PsiTypeParameter[] typeParameters = method.getTypeParameters();
|
||||
PsiType[] typeArguments = expression.getTypeArguments();
|
||||
updateNullabilityOfTypeArguments(typeParameters, typeArguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNewExpression(@NotNull PsiNewExpression expression) {
|
||||
if (KotlinPluginModeProvider.Companion.isK1Mode()) return;
|
||||
super.visitNewExpression(expression);
|
||||
|
||||
// Update array initializer component type nullability
|
||||
// based on the nullability of initializing member expressions
|
||||
if (!(expression.getType() instanceof PsiArrayType arrayType)) return;
|
||||
PsiType componentType = arrayType.getComponentType();
|
||||
|
||||
PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
|
||||
if (arrayInitializer == null) return;
|
||||
|
||||
PsiType arrayInitializerType = arrayInitializer.getType();
|
||||
if (!(arrayInitializerType instanceof PsiArrayType arrayInitializerArrayType)) return;
|
||||
PsiType arrayInitializerComponentType = arrayInitializerArrayType.getComponentType();
|
||||
|
||||
PsiExpression[] initializers = arrayInitializer.getInitializers();
|
||||
if (initializers.length == 0) return;
|
||||
|
||||
for (PsiExpression initializer : initializers) {
|
||||
Nullability nullability = NullabilityUtil.getExpressionNullability(initializer, true);
|
||||
if (nullability == Nullability.NULLABLE) {
|
||||
registerNullableType(componentType);
|
||||
registerNullableType(arrayInitializerComponentType);
|
||||
return;
|
||||
} else if (nullability == Nullability.UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, all array initializer expressions are not-null
|
||||
registerNotNullType(componentType);
|
||||
registerNotNullType(arrayInitializerComponentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider.Companion.isK1Mode
|
||||
import org.jetbrains.kotlin.idea.base.psi.kotlinFqName
|
||||
import org.jetbrains.kotlin.idea.j2k.content
|
||||
import org.jetbrains.kotlin.j2k.Nullability.NotNull
|
||||
import org.jetbrains.kotlin.j2k.Nullability.*
|
||||
import org.jetbrains.kotlin.j2k.ReferenceSearcher
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -63,16 +63,16 @@ class JavaToJKTreeBuilder(
|
||||
private val declarationMapper = DeclarationMapper(expressionTreeMapper, withBody = bodyFilter == null)
|
||||
private val formattingCollector = FormattingCollector()
|
||||
|
||||
// Per-file property with collected nullability information for various declarations.
|
||||
// Per-file property with collected nullability information.
|
||||
// Needs to be flushed before building the tree for each root element.
|
||||
private var declarationNullabilityInfo: DeclarationNullabilityInfo? = null
|
||||
private var nullabilityInfo: NullabilityInfo? = null
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance("@org.jetbrains.kotlin.nj2k.JavaToJKTreeBuilder")
|
||||
}
|
||||
|
||||
fun buildTree(psi: PsiElement, saveImports: Boolean): JKTreeRoot? {
|
||||
declarationNullabilityInfo = null
|
||||
nullabilityInfo = null
|
||||
|
||||
return when (psi) {
|
||||
is PsiJavaFile -> psi.toJK()
|
||||
@@ -1160,8 +1160,13 @@ class JavaToJKTreeBuilder(
|
||||
LOG.error(t)
|
||||
}
|
||||
|
||||
declarationNullabilityInfo = DeclarationNullabilityInfo(nullityInferrer.nullableTypes, nullityInferrer.notNullTypes)
|
||||
typeFactory.declarationNullabilityInfo = declarationNullabilityInfo
|
||||
nullabilityInfo = NullabilityInfo(
|
||||
nullityInferrer.nullableTypes,
|
||||
nullityInferrer.notNullTypes,
|
||||
nullityInferrer.nullableElements,
|
||||
nullityInferrer.notNullElements
|
||||
)
|
||||
typeFactory.nullabilityInfo = nullabilityInfo
|
||||
}
|
||||
|
||||
private fun PsiImportList?.toJK(saveImports: Boolean): JKImportList =
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.nj2k
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix
|
||||
import com.intellij.ide.actions.QualifiedNameProviderUtil
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.infos.MethodCandidateInfo
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.permissions.KaAllowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.analysis.api.permissions.allowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider.Companion.isK2Mode
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.productionOrTestSourceModuleInfo
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.toKaModule
|
||||
@@ -22,12 +26,10 @@ import org.jetbrains.kotlin.nj2k.J2KConversionPhase.*
|
||||
import org.jetbrains.kotlin.nj2k.externalCodeProcessing.NewExternalCodeProcessing
|
||||
import org.jetbrains.kotlin.nj2k.printing.JKCodeBuilder
|
||||
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtImportList
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class NewJavaToKotlinConverter(
|
||||
val project: Project,
|
||||
@@ -37,7 +39,7 @@ class NewJavaToKotlinConverter(
|
||||
) : JavaToKotlinConverter() {
|
||||
val phasesCount = J2KConversionPhase.entries.size
|
||||
val referenceSearcher: ReferenceSearcher = IdeaReferenceSearcher
|
||||
private val phaseDescription: String = KotlinNJ2KBundle.message("phase.converting.j2k")
|
||||
private val phaseDescription: String = KotlinNJ2KBundle.message("j2k.phase.converting")
|
||||
|
||||
override fun filesToKotlin(
|
||||
files: List<PsiJavaFile>,
|
||||
@@ -57,7 +59,14 @@ class NewJavaToKotlinConverter(
|
||||
postprocessorExtensions: List<J2kPostprocessorExtension>
|
||||
): FilesResult {
|
||||
val withProgressProcessor = NewJ2kWithProgressProcessor(progressIndicator, files, postProcessor.phasesCount + phasesCount)
|
||||
|
||||
return withProgressProcessor.process {
|
||||
// TODO looks like the progress dialog doesn't appear immediately, but should
|
||||
withProgressProcessor.updateState(fileIndex = null, phase = PREPROCESSING, KotlinNJ2KBundle.message("j2k.phase.preprocessing"))
|
||||
if (isK2Mode()) {
|
||||
runK2Preprocessing(files)
|
||||
}
|
||||
|
||||
PreprocessorExtensionsRunner.runProcessors(project, files, preprocessorExtensions)
|
||||
|
||||
val (results, externalCodeProcessing, context) = runReadAction {
|
||||
@@ -83,6 +92,52 @@ class NewJavaToKotlinConverter(
|
||||
}
|
||||
}
|
||||
|
||||
// A preprocessing to add explicit type arguments (needed for K2 nullability inference).
|
||||
// Resolve is done as a separate step for optimization.
|
||||
private fun runK2Preprocessing(files: List<PsiJavaFile>) {
|
||||
val set = mutableSetOf<PsiMethodCallExpression>()
|
||||
|
||||
runReadAction {
|
||||
for (file in files) {
|
||||
file.accept(object : JavaRecursiveElementWalkingVisitor() {
|
||||
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
|
||||
super.visitMethodCallExpression(expression)
|
||||
|
||||
if (expression.typeArguments.isNotEmpty()) return
|
||||
|
||||
val resolveResult = expression.resolveMethodGenerics()
|
||||
if (resolveResult is MethodCandidateInfo && resolveResult.isApplicable) {
|
||||
val method = resolveResult.element
|
||||
if (method.isConstructor || !method.hasTypeParameters()) return
|
||||
|
||||
// Avoid incorrect type arguments insertion that will lead to red code
|
||||
QualifiedNameProviderUtil.getQualifiedName(method)?.let { methodName ->
|
||||
if (methodName.startsWith("java.util.stream.Stream#collect") ||
|
||||
methodName.startsWith("java.util.stream.Collectors")
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set += expression
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
runUndoTransparentActionInEdt(inWriteAction = true) {
|
||||
for (expression in set) {
|
||||
val typeArgumentList = AddTypeArgumentsFix.addTypeArguments(expression, null, false)
|
||||
?.safeAs<PsiMethodCallExpression>()
|
||||
?.typeArgumentList
|
||||
?: continue
|
||||
|
||||
expression.typeArgumentList.replace(typeArgumentList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KaAllowAnalysisOnEdt::class)
|
||||
fun elementsToKotlin(
|
||||
inputElements: List<PsiElement>,
|
||||
@@ -263,8 +318,9 @@ private fun WithProgressProcessor.updateState(fileIndex: Int?, phase: J2KConvers
|
||||
}
|
||||
|
||||
private enum class J2KConversionPhase(val phaseNumber: Int) {
|
||||
BUILD_AST(0),
|
||||
RUN_CONVERSIONS(1),
|
||||
PRINT_CODE(2),
|
||||
CREATE_FILES(3)
|
||||
PREPROCESSING(0),
|
||||
BUILD_AST(1),
|
||||
RUN_CONVERSIONS(2),
|
||||
PRINT_CODE(3),
|
||||
CREATE_FILES(4)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.j2k.Nullability.NotNull
|
||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||
import org.jetbrains.kotlin.nj2k.RecursiveConversion
|
||||
import org.jetbrains.kotlin.nj2k.getDfaNullability
|
||||
import org.jetbrains.kotlin.nj2k.getExpressionDfaNullability
|
||||
import org.jetbrains.kotlin.nj2k.psi
|
||||
import org.jetbrains.kotlin.nj2k.tree.*
|
||||
import org.jetbrains.kotlin.nj2k.types.updateNullability
|
||||
@@ -56,7 +56,7 @@ class NullabilityConversion(context: NewJ2kConverterContext) : RecursiveConversi
|
||||
|
||||
// If Java DFA knows that the cast expression is not null, it is safe to cast to the not-null type
|
||||
psiCastedExpression != null -> {
|
||||
val dfaNullability = getDfaNullability(psiCastedExpression)
|
||||
val dfaNullability = getExpressionDfaNullability(psiCastedExpression)
|
||||
dfaNullability == DfaNullability.NOT_NULL
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,47 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.nj2k
|
||||
|
||||
import com.intellij.codeInsight.Nullability
|
||||
import com.intellij.codeInspection.dataFlow.CommonDataflow
|
||||
import com.intellij.codeInspection.dataFlow.DfaNullability
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil
|
||||
import com.intellij.codeInspection.dataFlow.types.DfReferenceType
|
||||
import com.intellij.psi.PsiExpression
|
||||
import com.intellij.psi.PsiPrimitiveType
|
||||
import com.intellij.psi.PsiReferenceExpression
|
||||
import com.intellij.psi.PsiType
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.*
|
||||
import com.siyeh.ig.psiutils.ExpectedTypeUtils
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtTypeParameter
|
||||
|
||||
/**
|
||||
* The set of possible declaration types is determined by [J2KNullityInferrer],
|
||||
* currently: PsiMethod, PsiLocalVariable, PsiParameter, PsiField.
|
||||
* A collection of types with known nullability as determined by [J2KNullityInferrer].
|
||||
* This information is used to set the nullability of created `JKType`s in [org.jetbrains.kotlin.nj2k.types.JKTypeFactory].
|
||||
*/
|
||||
internal data class DeclarationNullabilityInfo(
|
||||
internal data class NullabilityInfo(
|
||||
val nullableTypes: Set<PsiType>,
|
||||
val notNullTypes: Set<PsiType>,
|
||||
val nullableElements: Set<PsiJavaCodeReferenceElement>,
|
||||
val notNullElements: Set<PsiJavaCodeReferenceElement>,
|
||||
)
|
||||
|
||||
// Only Kotlin type parameters are supported
|
||||
internal fun getTypeParameterNullability(psiTypeParameter: PsiTypeParameter): Nullability {
|
||||
val ktTypeParameter = (psiTypeParameter as? KtLightDeclaration<*, *>)?.kotlinOrigin as? KtTypeParameter
|
||||
?: return Nullability.UNKNOWN
|
||||
|
||||
analyze(ktTypeParameter) {
|
||||
for (upperBound in ktTypeParameter.symbol.upperBounds) {
|
||||
if (upperBound.nullability == KaTypeNullability.NON_NULLABLE) {
|
||||
return Nullability.NOT_NULL
|
||||
}
|
||||
}
|
||||
|
||||
return Nullability.NULLABLE
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isUsedInAutoUnboxingContext(expr: PsiReferenceExpression): Boolean {
|
||||
val exprType = expr.type
|
||||
if (!TypeConversionUtil.isAssignableFromPrimitiveWrapper(exprType)) return false
|
||||
@@ -31,9 +53,19 @@ internal fun isUsedInAutoUnboxingContext(expr: PsiReferenceExpression): Boolean
|
||||
return expectedType.isAssignableFrom(unboxedType)
|
||||
}
|
||||
|
||||
internal fun getDfaNullability(expr: PsiExpression): DfaNullability? {
|
||||
// The result is cached by the Java DFA itself (this function is still hot, though)
|
||||
internal fun getExpressionDfaNullability(expr: PsiExpression): DfaNullability? {
|
||||
val dataflowResult = CommonDataflow.getDataflowResult(expr) ?: return null
|
||||
val dfType = dataflowResult.getDfType(expr)
|
||||
if (dfType !is DfReferenceType) return null
|
||||
return dfType.getNullability()
|
||||
}
|
||||
}
|
||||
|
||||
// This function should not be called very often, but the computation is expensive, so it's better to cache it anyway
|
||||
internal fun getMethodNullabilityByDfa(method: PsiMethod): Nullability {
|
||||
return CachedValuesManager.getManager(method.project).getCachedValue(method, METHOD_NULLABILITY_KEY, {
|
||||
CachedValueProvider.Result(DfaUtil.inferMethodNullability(method), PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}, false)
|
||||
}
|
||||
|
||||
private val METHOD_NULLABILITY_KEY = Key.create<CachedValue<Nullability>>("METHOD_NULLABILITY")
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package org.jetbrains.kotlin.nj2k.types
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaClassType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaStarTypeProjection
|
||||
@@ -12,7 +13,7 @@ import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.j2k.Nullability
|
||||
import org.jetbrains.kotlin.j2k.Nullability.*
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.nj2k.DeclarationNullabilityInfo
|
||||
import org.jetbrains.kotlin.nj2k.NullabilityInfo
|
||||
import org.jetbrains.kotlin.nj2k.JKSymbolProvider
|
||||
import org.jetbrains.kotlin.nj2k.symbols.JKClassSymbol
|
||||
import org.jetbrains.kotlin.nj2k.symbols.JKTypeParameterSymbol
|
||||
@@ -20,7 +21,7 @@ import org.jetbrains.kotlin.nj2k.symbols.JKUnresolvedClassSymbol
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
|
||||
class JKTypeFactory(val symbolProvider: JKSymbolProvider) {
|
||||
internal var declarationNullabilityInfo: DeclarationNullabilityInfo? = null
|
||||
internal var nullabilityInfo: NullabilityInfo? = null
|
||||
|
||||
fun fromPsiType(type: PsiType): JKType = createPsiType(type)
|
||||
|
||||
@@ -68,13 +69,7 @@ class JKTypeFactory(val symbolProvider: JKSymbolProvider) {
|
||||
val types by lazy(LazyThreadSafetyMode.NONE) { DefaultTypes() }
|
||||
|
||||
private fun createPsiType(type: PsiType): JKType {
|
||||
val nullability = declarationNullabilityInfo?.let {
|
||||
when {
|
||||
it.nullableTypes.contains(type) -> Nullable
|
||||
it.notNullTypes.contains(type) -> NotNull
|
||||
else -> Default
|
||||
}
|
||||
} ?: Default
|
||||
val nullability = getNullability(type)
|
||||
|
||||
return when (type) {
|
||||
is PsiClassType -> {
|
||||
@@ -147,6 +142,17 @@ class JKTypeFactory(val symbolProvider: JKSymbolProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNullability(type: PsiType): Nullability {
|
||||
val info = nullabilityInfo ?: return Default
|
||||
val referenceElement = if (type is PsiClassReferenceType) type.reference else null
|
||||
val nullability = when {
|
||||
info.nullableTypes.contains(type) || info.nullableElements.contains(referenceElement) -> Nullable
|
||||
info.notNullTypes.contains(type) || info.notNullElements.contains(referenceElement) -> NotNull
|
||||
else -> Default
|
||||
}
|
||||
return nullability
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
private fun createKaType(type: KaType): JKType {
|
||||
return when (type) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_K2
|
||||
// !FORCE_NOT_NULL_TYPES: false
|
||||
// !SPECIFY_LOCAL_VARIABLE_TYPE_BY_DEFAULT: true
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_K2
|
||||
import java.util.HashSet;
|
||||
|
||||
class Foo {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO: `name` parameter should be nullable in K2, looks like a bug in PsiClassType#getPsiContext()
|
||||
public class TestClass {
|
||||
private static String getCheckKey(String category, String name, boolean createWithProject) {
|
||||
return category + ':' + name + ':' + createWithProject;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// TODO: `name` parameter should be nullable in K2, looks like a bug in PsiClassType#getPsiContext()
|
||||
object TestClass {
|
||||
private fun getCheckKey(category: String?, name: String?, createWithProject: Boolean): String {
|
||||
private fun getCheckKey(category: String?, name: String, createWithProject: Boolean): String {
|
||||
return category + ':' + name + ':' + createWithProject
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO: `name` parameter should be nullable in K2, looks like a bug in PsiClassType#getPsiContext()
|
||||
object TestClass {
|
||||
private fun getCheckKey(category: String, name: String, createWithProject: Boolean): String {
|
||||
return "$category:$name:$createWithProject"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package demo
|
||||
|
||||
internal class Test {
|
||||
fun test(vararg args: Any?) {
|
||||
var args = args
|
||||
args = arrayOf<Int?>(1, 2, 3)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@ package demo
|
||||
internal class Test {
|
||||
fun test(vararg args: Any?) {
|
||||
var args = args
|
||||
args = arrayOf<Int?>(1, 2, 3)
|
||||
args = arrayOf<Int>(1, 2, 3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_K2
|
||||
// !ADD_JAVA_API
|
||||
import javaApi.WithVarargConstructor;
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
internal class C {
|
||||
var s: String = ""
|
||||
}
|
||||
|
||||
internal class D {
|
||||
fun foo(c: C) {
|
||||
if (null == c.s) {
|
||||
println("null")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
internal class C {
|
||||
var s: String = ""
|
||||
var s: String? = ""
|
||||
}
|
||||
|
||||
internal class D {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// JVM_TARGET: 17
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class J {
|
||||
public void test(String s) {
|
||||
new D(s);
|
||||
}
|
||||
}
|
||||
|
||||
record D(@NotNull String s) {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// ERROR: Primary constructor of data class must only have property ('val' / 'var') parameters.
|
||||
// ERROR: Non-constructor properties with backing field in '@JvmRecord' class are prohibited.
|
||||
class J {
|
||||
fun test(s: String) {
|
||||
D(s)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
internal data class D(s: String) {
|
||||
val s: String
|
||||
|
||||
init {
|
||||
this.s = s
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class J {
|
||||
fun test(s: String) {
|
||||
D(s)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
internal data class D(val s: String)
|
||||
@@ -0,0 +1,15 @@
|
||||
// JVM_TARGET: 17
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class C {
|
||||
private final ArrayList<String> field = new ArrayList<>();
|
||||
|
||||
public void foo() {
|
||||
new D(field);
|
||||
}
|
||||
}
|
||||
|
||||
record D(@NotNull ArrayList<@NotNull String> param) {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// ERROR: Primary constructor of data class must only have property ('val' / 'var') parameters.
|
||||
// ERROR: Non-constructor properties with backing field in '@JvmRecord' class are prohibited.
|
||||
class C {
|
||||
private val field = ArrayList<String>()
|
||||
|
||||
fun foo() {
|
||||
D(field)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
internal data class D(param: ArrayList<String>) {
|
||||
val param: ArrayList<String>
|
||||
|
||||
init {
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class C {
|
||||
private val field = ArrayList<String>()
|
||||
|
||||
fun foo() {
|
||||
D(field)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
internal data class D(val param: ArrayList<String>)
|
||||
@@ -0,0 +1,54 @@
|
||||
// TODO support array initializers
|
||||
|
||||
class ArrayArgument {
|
||||
void test(String[] array) {
|
||||
for (String s : array) {
|
||||
System.out.println(s.hashCode());
|
||||
}
|
||||
|
||||
takesArray(array);
|
||||
}
|
||||
|
||||
private void takesArray(String[] array) {
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayMethodCall {
|
||||
void test() {
|
||||
String[] array = strings();
|
||||
|
||||
for (String s : array) {
|
||||
System.out.println(s.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
private String[] strings() {
|
||||
return strings2();
|
||||
}
|
||||
|
||||
private String[] strings2() {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayParameter {
|
||||
void test(String[] param) {
|
||||
String[] array = param;
|
||||
|
||||
for (String s : array) {
|
||||
System.out.println(s.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayField {
|
||||
String[] field = new String[0];
|
||||
|
||||
void test() {
|
||||
String[] array = field;
|
||||
|
||||
for (String s : array) {
|
||||
System.out.println(s.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// ERROR: Initializer type mismatch: expected 'kotlin.Array<kotlin.String>', actual 'kotlin.Array<kotlin.String?>'.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.Array<T?>', but 'kotlin.Array<kotlin.String>' was expected.
|
||||
// ERROR: Return type mismatch: expected 'kotlin.Array<kotlin.String>', actual 'kotlin.Array<kotlin.String?>'.
|
||||
// TODO support array initializers
|
||||
internal class ArrayArgument {
|
||||
fun test(array: Array<String>) {
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
|
||||
takesArray(array)
|
||||
}
|
||||
|
||||
private fun takesArray(array: Array<String>?) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayMethodCall {
|
||||
fun test() {
|
||||
val array = strings()
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
|
||||
private fun strings(): Array<String> {
|
||||
return strings2()
|
||||
}
|
||||
|
||||
private fun strings2(): Array<String> {
|
||||
return arrayOfNulls<String>(0)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayParameter {
|
||||
fun test(param: Array<String>) {
|
||||
val array = param
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayField {
|
||||
var field: Array<String> = arrayOfNulls<String>(0)
|
||||
|
||||
fun test() {
|
||||
val array = field
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// TODO support array initializers
|
||||
internal class ArrayArgument {
|
||||
fun test(array: Array<String>) {
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
|
||||
takesArray(array)
|
||||
}
|
||||
|
||||
private fun takesArray(array: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayMethodCall {
|
||||
fun test() {
|
||||
val array = strings()
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
|
||||
private fun strings(): Array<String?> {
|
||||
return strings2()
|
||||
}
|
||||
|
||||
private fun strings2(): Array<String?> {
|
||||
return arrayOfNulls(0)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayParameter {
|
||||
fun test(param: Array<String>) {
|
||||
val array = param
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayField {
|
||||
var field: Array<String?> = arrayOfNulls(0)
|
||||
|
||||
fun test() {
|
||||
val array = field
|
||||
|
||||
for (s in array) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class ArrayField {
|
||||
void test() {
|
||||
String[] array = new String[0];
|
||||
|
||||
for (String s : array) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// ERROR: Initializer type mismatch: expected 'kotlin.Array<kotlin.String>', actual 'kotlin.Array<kotlin.String?>'.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.Array<T?>', but 'kotlin.Array<kotlin.String>' was expected.
|
||||
internal class ArrayField {
|
||||
fun test() {
|
||||
val array: Array<String> = arrayOfNulls<String>(0)
|
||||
|
||||
for (s in array) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
|
||||
internal class ArrayField {
|
||||
fun test() {
|
||||
val array = arrayOfNulls<String>(0)
|
||||
|
||||
for (s in array) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class AssignmentLocal {
|
||||
ArrayList<String> field = new ArrayList<>();
|
||||
|
||||
void test(ArrayList<String> param) {
|
||||
ArrayList<String> local = field;
|
||||
if (local.isEmpty()) {
|
||||
local = initLocal();
|
||||
} else {
|
||||
local = param;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<@NotNull String> initLocal() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
class AssignmentLocalReverse {
|
||||
ArrayList<String> field = new ArrayList<>();
|
||||
|
||||
void test(ArrayList<String> param) {
|
||||
ArrayList<@NotNull String> local = field;
|
||||
if (local.isEmpty()) {
|
||||
local = initLocal();
|
||||
} else {
|
||||
local = param;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> initLocal() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
class AssignmentField {
|
||||
ArrayList<String> field = new ArrayList<>();
|
||||
|
||||
void test(ArrayList<String> param) {
|
||||
if (field.isEmpty()) {
|
||||
field = initField();
|
||||
} else {
|
||||
field = param;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<@NotNull String> initField() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
class AssignmentFieldReverse {
|
||||
ArrayList<@NotNull String> field = new ArrayList<>();
|
||||
|
||||
void test(ArrayList<String> param) {
|
||||
if (field.isEmpty()) {
|
||||
field = initField();
|
||||
} else {
|
||||
field = param;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> initField() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
internal class AssignmentLocal {
|
||||
var field: ArrayList<String> = ArrayList<String>()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
var local = field
|
||||
if (local.isEmpty()) {
|
||||
local = initLocal()
|
||||
} else {
|
||||
local = param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLocal(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentLocalReverse {
|
||||
var field: ArrayList<String> = ArrayList<String>()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
var local = field
|
||||
if (local.isEmpty()) {
|
||||
local = initLocal()
|
||||
} else {
|
||||
local = param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLocal(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentField {
|
||||
var field: ArrayList<String> = ArrayList<String>()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
if (field.isEmpty()) {
|
||||
field = initField()
|
||||
} else {
|
||||
field = param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initField(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentFieldReverse {
|
||||
var field: ArrayList<String> = ArrayList<String>()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
if (field.isEmpty()) {
|
||||
field = initField()
|
||||
} else {
|
||||
field = param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initField(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
internal class AssignmentLocal {
|
||||
var field: ArrayList<String> = ArrayList()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
var local = field
|
||||
local = if (local.isEmpty()) {
|
||||
initLocal()
|
||||
} else {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLocal(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentLocalReverse {
|
||||
var field: ArrayList<String> = ArrayList()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
var local = field
|
||||
local = if (local.isEmpty()) {
|
||||
initLocal()
|
||||
} else {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLocal(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentField {
|
||||
var field: ArrayList<String> = ArrayList()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
field = if (field.isEmpty()) {
|
||||
initField()
|
||||
} else {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initField(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssignmentFieldReverse {
|
||||
var field: ArrayList<String> = ArrayList()
|
||||
|
||||
fun test(param: ArrayList<String>) {
|
||||
field = if (field.isEmpty()) {
|
||||
initField()
|
||||
} else {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
||||
private fun initField(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Main {
|
||||
public ArrayList<String> f1() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public ArrayList<String> f2() {
|
||||
ArrayList<String> list = f1();
|
||||
f3(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public void f3(ArrayList<String> list) {
|
||||
for (String item : list) {
|
||||
System.out.println(item.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Main {
|
||||
fun f1(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
|
||||
fun f2(): ArrayList<String> {
|
||||
val list = f1()
|
||||
f3(list)
|
||||
return list
|
||||
}
|
||||
|
||||
fun f3(list: ArrayList<String>) {
|
||||
for (item in list) {
|
||||
println(item.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Main {
|
||||
fun f1(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
fun f2(): ArrayList<String> {
|
||||
val list = f1()
|
||||
f3(list)
|
||||
return list
|
||||
}
|
||||
|
||||
fun f3(list: ArrayList<String>) {
|
||||
for (item in list) {
|
||||
println(item.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class J {
|
||||
@Nullable ArrayList<@Nullable String> field1;
|
||||
@Nullable ArrayList<@NotNull String> field2;
|
||||
@NotNull ArrayList<@Nullable String> field3 = new ArrayList<>();
|
||||
@NotNull ArrayList<@NotNull String> field4 = new ArrayList<>();
|
||||
|
||||
public @Nullable ArrayList<@Nullable String> return1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable ArrayList<@NotNull String> return2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @NotNull ArrayList<@Nullable String> return3() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public @NotNull ArrayList<@NotNull String> return4() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public void argument1(@Nullable ArrayList<@Nullable String> x) {
|
||||
}
|
||||
|
||||
public void argument2(@Nullable ArrayList<@NotNull String> x) {
|
||||
}
|
||||
|
||||
public void argument3(@NotNull ArrayList<@Nullable String> x) {
|
||||
}
|
||||
|
||||
public void argument4(@NotNull ArrayList<@NotNull String> x) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Foo {
|
||||
void testAssignment(J j) {
|
||||
ArrayList<String> l1 = j.return1();
|
||||
ArrayList<String> l2 = j.return2();
|
||||
ArrayList<String> l3 = j.return3();
|
||||
ArrayList<String> l4 = j.return4();
|
||||
|
||||
ArrayList<String> l5 = j.field1;
|
||||
ArrayList<String> l6 = j.field2;
|
||||
ArrayList<String> l7 = j.field3;
|
||||
ArrayList<String> l8 = j.field4;
|
||||
}
|
||||
|
||||
void testArgument(
|
||||
J j,
|
||||
ArrayList<String> l1,
|
||||
ArrayList<String> l2,
|
||||
ArrayList<String> l3,
|
||||
ArrayList<String> l4
|
||||
) {
|
||||
j.argument1(l1);
|
||||
j.argument2(l2);
|
||||
j.argument3(l3);
|
||||
j.argument4(l4);
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn1(J j) {
|
||||
return j.return1();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn2(J j) {
|
||||
return j.return2();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn3(J j) {
|
||||
return j.return3();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn4(J j) {
|
||||
return j.return4();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
class Foo {
|
||||
fun testAssignment(j: J) {
|
||||
val l1 = j.return1()
|
||||
val l2 = j.return2()
|
||||
val l3 = j.return3()
|
||||
val l4 = j.return4()
|
||||
|
||||
val l5 = j.field1
|
||||
val l6 = j.field2
|
||||
val l7 = j.field3
|
||||
val l8 = j.field4
|
||||
}
|
||||
|
||||
fun testArgument(
|
||||
j: J,
|
||||
l1: ArrayList<String?>?,
|
||||
l2: ArrayList<String?>?,
|
||||
l3: ArrayList<String?>,
|
||||
l4: ArrayList<String?>
|
||||
) {
|
||||
j.argument1(l1)
|
||||
j.argument2(l2)
|
||||
j.argument3(l3)
|
||||
j.argument4(l4)
|
||||
}
|
||||
|
||||
fun testReturn1(j: J): ArrayList<String?>? {
|
||||
return j.return1()
|
||||
}
|
||||
|
||||
fun testReturn2(j: J): ArrayList<String?>? {
|
||||
return j.return2()
|
||||
}
|
||||
|
||||
fun testReturn3(j: J): ArrayList<String?> {
|
||||
return j.return3()
|
||||
}
|
||||
|
||||
fun testReturn4(j: J): ArrayList<String?> {
|
||||
return j.return4()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
class Foo {
|
||||
fun testAssignment(j: J) {
|
||||
val l1 = j.return1()
|
||||
val l2 = j.return2()
|
||||
val l3 = j.return3()
|
||||
val l4 = j.return4()
|
||||
|
||||
val l5 = j.field1
|
||||
val l6 = j.field2
|
||||
val l7 = j.field3
|
||||
val l8 = j.field4
|
||||
}
|
||||
|
||||
fun testArgument(
|
||||
j: J,
|
||||
l1: ArrayList<String?>?,
|
||||
l2: ArrayList<String?>?,
|
||||
l3: ArrayList<String?>,
|
||||
l4: ArrayList<String?>
|
||||
) {
|
||||
j.argument1(l1)
|
||||
j.argument2(l2)
|
||||
j.argument3(l3)
|
||||
j.argument4(l4)
|
||||
}
|
||||
|
||||
fun testReturn1(j: J): ArrayList<String>? {
|
||||
return j.return1()
|
||||
}
|
||||
|
||||
fun testReturn2(j: J): ArrayList<String>? {
|
||||
return j.return2()
|
||||
}
|
||||
|
||||
fun testReturn3(j: J): ArrayList<String> {
|
||||
return j.return3()
|
||||
}
|
||||
|
||||
fun testReturn4(j: J): ArrayList<String> {
|
||||
return j.return4()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
class K {
|
||||
fun return1(): ArrayList<String?>? = null
|
||||
fun return2(): ArrayList<String>? = null
|
||||
fun return3(): ArrayList<String?> = ArrayList()
|
||||
fun return4(): ArrayList<String> = ArrayList()
|
||||
|
||||
fun argument1(x: ArrayList<String?>?) {}
|
||||
fun argument2(x: ArrayList<String>?) {}
|
||||
fun argument3(x: ArrayList<String?>) {}
|
||||
fun argument4(x: ArrayList<String>) {}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Foo {
|
||||
void testAssignment(K k) {
|
||||
ArrayList<String> l1 = k.return1();
|
||||
ArrayList<String> l2 = k.return2();
|
||||
ArrayList<String> l3 = k.return3();
|
||||
ArrayList<String> l4 = k.return4();
|
||||
}
|
||||
|
||||
void testArgument(
|
||||
K k,
|
||||
ArrayList<String> l1,
|
||||
ArrayList<String> l2,
|
||||
ArrayList<String> l3,
|
||||
ArrayList<String> l4
|
||||
) {
|
||||
k.argument1(l1);
|
||||
k.argument2(l2);
|
||||
k.argument3(l3);
|
||||
k.argument4(l4);
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn1(K k) {
|
||||
return k.return1();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn2(K k) {
|
||||
return k.return2();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn3(K k) {
|
||||
return k.return3();
|
||||
}
|
||||
|
||||
ArrayList<String> testReturn4(K k) {
|
||||
return k.return4();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// ERROR: Return type mismatch: expected 'java.util.ArrayList<kotlin.String?>', actual 'java.util.ArrayList<kotlin.String>'.
|
||||
// ERROR: Return type mismatch: expected 'java.util.ArrayList<kotlin.String?>?', actual 'java.util.ArrayList<kotlin.String>?'.
|
||||
// ERROR: Argument type mismatch: actual type is 'java.util.ArrayList<kotlin.String?>?', but 'java.util.ArrayList<kotlin.String>?' was expected.
|
||||
// ERROR: Argument type mismatch: actual type is 'java.util.ArrayList<kotlin.String?>', but 'java.util.ArrayList<kotlin.String>' was expected.
|
||||
// ERROR: Initializer type mismatch: expected 'java.util.ArrayList<kotlin.String?>?', actual 'java.util.ArrayList<kotlin.String>?'.
|
||||
// ERROR: Type mismatch: inferred type is 'java.util.ArrayList<kotlin.String>?', but 'java.util.ArrayList<kotlin.String?>?' was expected.
|
||||
// ERROR: Initializer type mismatch: expected 'java.util.ArrayList<kotlin.String?>', actual 'java.util.ArrayList<kotlin.String>'.
|
||||
// ERROR: Type mismatch: inferred type is 'java.util.ArrayList<kotlin.String>', but 'java.util.ArrayList<kotlin.String?>' was expected.
|
||||
class Foo {
|
||||
fun testAssignment(k: K) {
|
||||
val l1 = k.return1()
|
||||
val l2: ArrayList<String?>? = k.return2()
|
||||
val l3 = k.return3()
|
||||
val l4: ArrayList<String?> = k.return4()
|
||||
}
|
||||
|
||||
fun testArgument(
|
||||
k: K,
|
||||
l1: ArrayList<String?>?,
|
||||
l2: ArrayList<String?>?,
|
||||
l3: ArrayList<String?>,
|
||||
l4: ArrayList<String?>
|
||||
) {
|
||||
k.argument1(l1)
|
||||
k.argument2(l2)
|
||||
k.argument3(l3)
|
||||
k.argument4(l4)
|
||||
}
|
||||
|
||||
fun testReturn1(k: K): ArrayList<String?>? {
|
||||
return k.return1()
|
||||
}
|
||||
|
||||
fun testReturn2(k: K): ArrayList<String?>? {
|
||||
return k.return2()
|
||||
}
|
||||
|
||||
fun testReturn3(k: K): ArrayList<String?> {
|
||||
return k.return3()
|
||||
}
|
||||
|
||||
fun testReturn4(k: K): ArrayList<String?> {
|
||||
return k.return4()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// ERROR: Type mismatch: inferred type is kotlin.collections.ArrayList<String?>? /* = java.util.ArrayList<String?>? */ but java.util.ArrayList<String>? was expected
|
||||
// ERROR: Type mismatch: inferred type is kotlin.collections.ArrayList<String?> /* = java.util.ArrayList<String?> */ but java.util.ArrayList<String> was expected
|
||||
class Foo {
|
||||
fun testAssignment(k: K) {
|
||||
val l1 = k.return1()
|
||||
val l2 = k.return2()
|
||||
val l3 = k.return3()
|
||||
val l4 = k.return4()
|
||||
}
|
||||
|
||||
fun testArgument(
|
||||
k: K,
|
||||
l1: ArrayList<String?>?,
|
||||
l2: ArrayList<String?>?,
|
||||
l3: ArrayList<String?>,
|
||||
l4: ArrayList<String?>
|
||||
) {
|
||||
k.argument1(l1)
|
||||
k.argument2(l2)
|
||||
k.argument3(l3)
|
||||
k.argument4(l4)
|
||||
}
|
||||
|
||||
fun testReturn1(k: K): ArrayList<String?>? {
|
||||
return k.return1()
|
||||
}
|
||||
|
||||
fun testReturn2(k: K): ArrayList<String>? {
|
||||
return k.return2()
|
||||
}
|
||||
|
||||
fun testReturn3(k: K): ArrayList<String?> {
|
||||
return k.return3()
|
||||
}
|
||||
|
||||
fun testReturn4(k: K): ArrayList<String> {
|
||||
return k.return4()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO handle the case when type argument is used in the method return type (make it not-null)
|
||||
public class J {
|
||||
Set<String> notNullSet = Collections.emptySet();
|
||||
|
||||
Collection<String> notNullCollection = Collections.emptyList();
|
||||
Collection<String> nullableCollection = Collections.emptyList();
|
||||
|
||||
List<@NotNull String> notNullList = List.of();
|
||||
List<@Nullable String> nullableList = List.of();
|
||||
|
||||
void foo() {
|
||||
for (String s : notNullSet) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
for (String s : notNullCollection) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
for (String s : nullableCollection) {
|
||||
if (s != null) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
takeNotNullCollection(Collections.emptyList());
|
||||
takeNotNullCollection(returnNotNullCollection());
|
||||
}
|
||||
|
||||
private void takeNotNullCollection(Collection<@NotNull String> strings) {
|
||||
}
|
||||
|
||||
Collection<String> returnNotNullCollection() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// ERROR: Return type mismatch: expected 'kotlin.collections.MutableCollection<kotlin.String>', actual 'kotlin.collections.MutableList<kotlin.String?>'.
|
||||
// ERROR: Initializer type mismatch: expected 'kotlin.collections.MutableCollection<kotlin.String>', actual 'kotlin.collections.MutableList<kotlin.String?>'.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.String?', but 'kotlin.String' was expected.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.String?', but 'kotlin.String' was expected.
|
||||
// ERROR: Initializer type mismatch: expected 'kotlin.collections.MutableSet<kotlin.String>', actual 'kotlin.collections.MutableSet<kotlin.String?>'.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.String?', but 'kotlin.String' was expected.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.String?', but 'kotlin.String' was expected.
|
||||
// TODO handle the case when type argument is used in the method return type (make it not-null)
|
||||
class J {
|
||||
var notNullSet: MutableSet<String> = mutableSetOf<String?>()
|
||||
|
||||
var notNullCollection: MutableCollection<String> = mutableListOf<String?>()
|
||||
var nullableCollection: MutableCollection<String?> = mutableListOf<String?>()
|
||||
|
||||
var notNullList: MutableList<String> = mutableListOf<String>()
|
||||
var nullableList: MutableList<String?> = mutableListOf<String?>()
|
||||
|
||||
fun foo() {
|
||||
for (s in notNullSet) {
|
||||
println(s.length)
|
||||
}
|
||||
for (s in notNullCollection) {
|
||||
println(s.length)
|
||||
}
|
||||
for (s in nullableCollection) {
|
||||
if (s != null) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
takeNotNullCollection(mutableListOf<String>())
|
||||
takeNotNullCollection(returnNotNullCollection())
|
||||
}
|
||||
|
||||
private fun takeNotNullCollection(strings: MutableCollection<String>?) {
|
||||
}
|
||||
|
||||
fun returnNotNullCollection(): MutableCollection<String> {
|
||||
return mutableListOf<String?>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// TODO handle the case when type argument is used in the method return type (make it not-null)
|
||||
class J {
|
||||
var notNullSet: Set<String> = emptySet()
|
||||
|
||||
var notNullCollection: Collection<String> = emptyList()
|
||||
var nullableCollection: Collection<String> = emptyList()
|
||||
|
||||
var notNullList: List<String> = listOf()
|
||||
var nullableList: List<String?> = listOf()
|
||||
|
||||
fun foo() {
|
||||
for (s in notNullSet) {
|
||||
println(s.length)
|
||||
}
|
||||
for (s in notNullCollection) {
|
||||
println(s.length)
|
||||
}
|
||||
for (s in nullableCollection) {
|
||||
if (s != null) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
takeNotNullCollection(emptyList())
|
||||
takeNotNullCollection(returnNotNullCollection())
|
||||
}
|
||||
|
||||
private fun takeNotNullCollection(strings: Collection<String>) {
|
||||
}
|
||||
|
||||
fun returnNotNullCollection(): Collection<String> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class MethodCallArgument {
|
||||
void test() {
|
||||
takesStrings(strings(), strings());
|
||||
}
|
||||
|
||||
ArrayList<@NotNull String> strings() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private void takesStrings(ArrayList<String> strings1, ArrayList<String> strings2) {
|
||||
}
|
||||
}
|
||||
|
||||
class MethodCallArgumentReverse {
|
||||
void test() {
|
||||
takesStrings(strings1(), strings2());
|
||||
}
|
||||
|
||||
ArrayList<String> strings1() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ArrayList<String> strings2() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private void takesStrings(ArrayList<@NotNull String> strings1, ArrayList<@NotNull String> strings2) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
internal class MethodCallArgument {
|
||||
fun test() {
|
||||
takesStrings(strings(), strings())
|
||||
}
|
||||
|
||||
fun strings(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
|
||||
private fun takesStrings(strings1: ArrayList<String>?, strings2: ArrayList<String>?) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MethodCallArgumentReverse {
|
||||
fun test() {
|
||||
takesStrings(strings1(), strings2())
|
||||
}
|
||||
|
||||
fun strings1(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
|
||||
fun strings2(): ArrayList<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
|
||||
private fun takesStrings(strings1: ArrayList<String>?, strings2: ArrayList<String>?) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
internal class MethodCallArgument {
|
||||
fun test() {
|
||||
takesStrings(strings(), strings())
|
||||
}
|
||||
|
||||
fun strings(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
private fun takesStrings(strings1: ArrayList<String>, strings2: ArrayList<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MethodCallArgumentReverse {
|
||||
fun test() {
|
||||
takesStrings(strings1(), strings2())
|
||||
}
|
||||
|
||||
fun strings1(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
fun strings2(): ArrayList<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
private fun takesStrings(strings1: ArrayList<String>, strings2: ArrayList<String>) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
public class C {
|
||||
public String[] stringsField = new String[]{"Hello", "World"};
|
||||
|
||||
public void field() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void param(String[] strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void local() {
|
||||
String[] stringsLocal = new String[]{"Hello", "World"};
|
||||
for (String s : stringsLocal) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// ERROR: Initializer type mismatch: expected 'kotlin.Array<kotlin.String?>', actual 'kotlin.Array<kotlin.String>'.
|
||||
// ERROR: Type mismatch: inferred type is 'kotlin.String?', but 'kotlin.String' was expected.
|
||||
class C {
|
||||
var stringsField: Array<String> = arrayOf<String>("Hello", "World")
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Array<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: Array<String?> = arrayOf<String>("Hello", "World")
|
||||
for (s in stringsLocal) {
|
||||
println(s!!.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
class C {
|
||||
var stringsField: Array<String> = arrayOf("Hello", "World")
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Array<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal = arrayOf("Hello", "World")
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import java.util.*;
|
||||
|
||||
class TestCollection {
|
||||
public Collection<String> stringsField = new ArrayList<>();
|
||||
|
||||
public void field() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void param(Collection<String> strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void local() {
|
||||
Collection<String> stringsLocal = new ArrayList<>();
|
||||
for (String s : stringsLocal) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestIterable {
|
||||
public Iterable<String> stringsField = new ArrayList<>();
|
||||
|
||||
public void field() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void param(Iterable<String> strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void local() {
|
||||
Iterable<String> stringsLocal = new ArrayList<>();
|
||||
for (String s : stringsLocal) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestList {
|
||||
public List<String> stringsField = new ArrayList<>();
|
||||
|
||||
public void field() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void param(List<String> strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void local() {
|
||||
List<String> stringsLocal = new ArrayList<>();
|
||||
for (String s : stringsLocal) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestSet {
|
||||
public Set<String> stringsField = new HashSet<>();
|
||||
|
||||
public void field() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void param(Set<String> strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void local() {
|
||||
Set<String> stringsLocal = new HashSet<>();
|
||||
for (String s : stringsLocal) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
internal class TestCollection {
|
||||
var stringsField: Collection<String> = ArrayList()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Collection<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: Collection<String> = ArrayList()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestIterable {
|
||||
var stringsField: Iterable<String> = ArrayList()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Iterable<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: Iterable<String> = ArrayList()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestList {
|
||||
var stringsField: List<String> = ArrayList()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: List<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: List<String> = ArrayList()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestSet {
|
||||
var stringsField: Set<String> = HashSet()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Set<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: Set<String> = HashSet()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
internal class TestCollection {
|
||||
var stringsField: MutableCollection<String> = ArrayList<String>()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: MutableCollection<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: MutableCollection<String> = ArrayList<String>()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestIterable {
|
||||
var stringsField: Iterable<String> = ArrayList<String>()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: Iterable<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: Iterable<String> = ArrayList<String>()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestList {
|
||||
var stringsField: MutableList<String> = ArrayList<String>()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: MutableList<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: MutableList<String> = ArrayList<String>()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestSet {
|
||||
var stringsField: MutableSet<String> = HashSet<String>()
|
||||
|
||||
fun field() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun param(strings: MutableSet<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
fun local() {
|
||||
val stringsLocal: MutableSet<String> = HashSet<String>()
|
||||
for (s in stringsLocal) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.*;
|
||||
|
||||
class TestMethodCall {
|
||||
public void test() {
|
||||
for (String s : returnStrings()) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
private Iterable<String> returnStrings() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
internal class TestMethodCall {
|
||||
fun test() {
|
||||
for (s in returnStrings()) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnStrings(): Iterable<String> {
|
||||
return ArrayList<String>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
internal class TestMethodCall {
|
||||
fun test() {
|
||||
for (s in returnStrings()) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnStrings(): Iterable<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.ArrayList;
|
||||
|
||||
class TypeArgumentOnly {
|
||||
private void notNull(ArrayList<@NotNull String> strings) {
|
||||
foo(strings);
|
||||
}
|
||||
|
||||
private void foo(ArrayList<String> strings) {
|
||||
}
|
||||
}
|
||||
|
||||
class DeeplyNotNull {
|
||||
private void notNull(@NotNull ArrayList<@NotNull String> strings) {
|
||||
foo(strings);
|
||||
}
|
||||
|
||||
// top-level ArrayList can still be nullable
|
||||
private void foo(ArrayList<String> strings) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
internal class TypeArgumentOnly {
|
||||
private fun notNull(strings: ArrayList<String>) {
|
||||
foo(strings)
|
||||
}
|
||||
|
||||
private fun foo(strings: ArrayList<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class DeeplyNotNull {
|
||||
private fun notNull(strings: ArrayList<String>) {
|
||||
foo(strings)
|
||||
}
|
||||
|
||||
// top-level ArrayList can still be nullable
|
||||
private fun foo(strings: ArrayList<String>) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
internal class TypeArgumentOnly {
|
||||
private fun notNull(strings: ArrayList<String>?) {
|
||||
foo(strings)
|
||||
}
|
||||
|
||||
private fun foo(strings: ArrayList<String>?) {
|
||||
}
|
||||
}
|
||||
|
||||
internal class DeeplyNotNull {
|
||||
private fun notNull(strings: ArrayList<String>) {
|
||||
foo(strings)
|
||||
}
|
||||
|
||||
// top-level ArrayList can still be nullable
|
||||
private fun foo(strings: ArrayList<String>?) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
internal class K {
|
||||
fun <T : Any> f(topic: Topic<T>) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class J {
|
||||
Topic<String> topic = new Topic<>();
|
||||
|
||||
void test(K k) {
|
||||
k.f(topic);
|
||||
k.<String>f(topic);
|
||||
}
|
||||
}
|
||||
|
||||
class Topic<T> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
internal class J {
|
||||
var topic: Topic<String> = Topic()
|
||||
|
||||
fun test(k: K) {
|
||||
k.f(topic)
|
||||
k.f(topic)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Topic<T>
|
||||
@@ -0,0 +1,12 @@
|
||||
// ERROR: Argument type mismatch: actual type is 'Topic<kotlin.String?>', but 'Topic<T>' was expected.
|
||||
// ERROR: Argument type mismatch: actual type is 'Topic<kotlin.String?>', but 'Topic<T>' was expected.
|
||||
internal class J {
|
||||
var topic: Topic<String?> = Topic<String?>()
|
||||
|
||||
fun test(k: K) {
|
||||
k.f<String>(topic)
|
||||
k.f<String>(topic)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Topic<T>
|
||||
@@ -0,0 +1,5 @@
|
||||
class K<T : Any> {
|
||||
}
|
||||
|
||||
class K2<T1, T2 : Any> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class J {
|
||||
private static final K<String> ka = new K<String>();
|
||||
private static final K<String> kb = new K<>();
|
||||
private static final K<Integer> kc = new K<>();
|
||||
private static final K<Object> kd = new K<Object>();
|
||||
|
||||
private static final K2<String, String> k2a = new K2<String, String>();
|
||||
private static final K2<String, String> k2b = new K2<>();
|
||||
private static final K2<String, Integer> k2c = new K2<>();
|
||||
private static final K2<Integer, String> k2d = new K2<>();
|
||||
private static final K2<Object, Object> k2e = new K2<Object, Object>();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
object J {
|
||||
private val ka = K<String>()
|
||||
private val kb = K<String>()
|
||||
private val kc = K<Int>()
|
||||
private val kd = K<Any>()
|
||||
|
||||
private val k2a = K2<String, String>()
|
||||
private val k2b = K2<String, String>()
|
||||
private val k2c = K2<String, Int>()
|
||||
private val k2d = K2<Int, String>()
|
||||
private val k2e = K2<Any, Any>()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
object J {
|
||||
private val ka = K<String>()
|
||||
private val kb = K<String>()
|
||||
private val kc = K<Int>()
|
||||
private val kd = K<Any>()
|
||||
|
||||
private val k2a = K2<String?, String>()
|
||||
private val k2b = K2<String?, String>()
|
||||
private val k2c = K2<String?, Int>()
|
||||
private val k2d = K2<Int?, String>()
|
||||
private val k2e = K2<Any?, Any>()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
public class C {
|
||||
public void foo(String[] strings) {
|
||||
bar(strings);
|
||||
}
|
||||
|
||||
public void bar(String[] strings) {
|
||||
baz(strings);
|
||||
}
|
||||
|
||||
public void baz(String[] strings) {
|
||||
for (String s : strings) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class C {
|
||||
fun foo(strings: Array<String>) {
|
||||
bar(strings)
|
||||
}
|
||||
|
||||
fun bar(strings: Array<String>) {
|
||||
baz(strings)
|
||||
}
|
||||
|
||||
fun baz(strings: Array<String>) {
|
||||
for (s in strings) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class C {
|
||||
private final HashMap<String, String> field = new HashMap<>();
|
||||
|
||||
public void foo() {
|
||||
new D(field);
|
||||
}
|
||||
}
|
||||
|
||||
class D {
|
||||
public D(HashMap<@NotNull String, @NotNull String> param) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class C {
|
||||
private val field = HashMap<String, String>()
|
||||
|
||||
fun foo() {
|
||||
D(field)
|
||||
}
|
||||
}
|
||||
|
||||
internal class D(param: HashMap<String, String>?)
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.*;
|
||||
|
||||
class J {
|
||||
public ArrayList<String> stringsField = new ArrayList<>();
|
||||
|
||||
public void test() {
|
||||
for (String s : stringsField) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> returnStrings() {
|
||||
return stringsField;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
internal class J {
|
||||
var stringsField: ArrayList<String> = ArrayList<String>()
|
||||
|
||||
fun test() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnStrings(): ArrayList<String> {
|
||||
return stringsField
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
internal class J {
|
||||
var stringsField: ArrayList<String> = ArrayList()
|
||||
|
||||
fun test() {
|
||||
for (s in stringsField) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnStrings(): ArrayList<String> {
|
||||
return stringsField
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import java.util.*;
|
||||
// NOTE: wrong error message is KT-69090
|
||||
|
||||
class J {
|
||||
private final ArrayList<String> strings = new ArrayList<>();
|
||||
|
||||
public void report(String s) {
|
||||
strings.add(s);
|
||||
}
|
||||
|
||||
public ArrayList<String> returnStrings() {
|
||||
return strings; // update return expression type from method return type
|
||||
}
|
||||
|
||||
public void test() {
|
||||
for (String s : returnStrings()) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// ERROR: Cannot access 'fun add(e: String!, elementData: (Array<Any!>..Array<out Any!>?), s: Int): Unit': it is private in 'java/util/ArrayList'.
|
||||
// NOTE: wrong error message is KT-69090
|
||||
internal class J {
|
||||
private val strings = ArrayList<String>()
|
||||
|
||||
fun report(s: String?) {
|
||||
strings.add(s)
|
||||
}
|
||||
|
||||
fun returnStrings(): ArrayList<String> {
|
||||
return strings // update return expression type from method return type
|
||||
}
|
||||
|
||||
fun test() {
|
||||
for (s in returnStrings()) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// NOTE: wrong error message is KT-69090
|
||||
internal class J {
|
||||
private val strings = ArrayList<String>()
|
||||
|
||||
fun report(s: String) {
|
||||
strings.add(s)
|
||||
}
|
||||
|
||||
fun returnStrings(): ArrayList<String> {
|
||||
return strings // update return expression type from method return type
|
||||
}
|
||||
|
||||
fun test() {
|
||||
for (s in returnStrings()) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class C {
|
||||
private final HashMap<String, String> field = new HashMap<>();
|
||||
|
||||
public void foo() {
|
||||
baz(field);
|
||||
}
|
||||
|
||||
public void baz(HashMap<@NotNull String, @NotNull String> param) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class C {
|
||||
private val field = HashMap<String, String>()
|
||||
|
||||
fun foo() {
|
||||
baz(field)
|
||||
}
|
||||
|
||||
fun baz(param: HashMap<String, String>?) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class J {
|
||||
private void test(ArrayList<String> strings) {
|
||||
ArrayList<String> strings1 = strings;
|
||||
ArrayList<String> strings2 = strings1;
|
||||
|
||||
for (String s : strings2) {
|
||||
System.out.println(s.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class J {
|
||||
private fun test(strings: ArrayList<String>) {
|
||||
val strings1 = strings
|
||||
val strings2 = strings1
|
||||
|
||||
for (s in strings2) {
|
||||
println(s.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class ReturnMethodCallExpr {
|
||||
@NotNull String test(ArrayList<String> param) {
|
||||
return param.get(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
internal class ReturnMethodCallExpr {
|
||||
fun test(param: ArrayList<String?>): String {
|
||||
return param.get(0)!!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
internal class ReturnMethodCallExpr {
|
||||
fun test(param: ArrayList<String>): String {
|
||||
return param[0]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class J {
|
||||
void foo(List<String> list, Map<String, String> map) {
|
||||
String s1 = list.get(0);
|
||||
String s2 = map.get("");
|
||||
|
||||
s1.length(); // not-null assertion
|
||||
s2.length(); // not-null assertion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class J {
|
||||
fun foo(list: List<String>, map: Map<String?, String?>) {
|
||||
val s1 = list[0]
|
||||
val s2 = map[""]
|
||||
|
||||
s1.length // not-null assertion
|
||||
s2!!.length // not-null assertion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class J {
|
||||
fun foo(list: MutableList<String>, map: MutableMap<String?, String>) {
|
||||
val s1 = list.get(0)
|
||||
val s2: String = map.get("")!!
|
||||
|
||||
s1.length // not-null assertion
|
||||
s2.length // not-null assertion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class J {
|
||||
public static <T> ArrayList<@Nullable T> nullableList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <T> ArrayList<@NotNull T> notNullList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <T> ArrayList<T> unknownList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <T> ArrayList<String> unrelatedList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Foo {
|
||||
void test() {
|
||||
ArrayList<String> nullableList = J.nullableList();
|
||||
ArrayList<String> notNullList = J.notNullList();
|
||||
|
||||
ArrayList<String> unknownListNullable = J.unknownList();
|
||||
ArrayList<String> unknownListNotNull = J.unknownList();
|
||||
|
||||
ArrayList<String> unrelatedListNotNull = J.unrelatedList();
|
||||
ArrayList<String> unrelatedList2 = J.unrelatedList();
|
||||
|
||||
for (String s : notNullList) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
for (String s : unknownListNotNull) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
for (String s : unknownListNullable) {
|
||||
if (s != null) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
|
||||
for (String s : unrelatedListNotNull) {
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
class Foo {
|
||||
fun test() {
|
||||
val nullableList = J.nullableList<String?>()
|
||||
val notNullList = J.notNullList<String?>()
|
||||
|
||||
val unknownListNullable = J.unknownList<String?>()
|
||||
val unknownListNotNull = J.unknownList<String?>()
|
||||
|
||||
val unrelatedListNotNull = J.unrelatedList<Any?>()
|
||||
val unrelatedList2 = J.unrelatedList<Any?>()
|
||||
|
||||
for (s in notNullList) {
|
||||
println(s.length)
|
||||
}
|
||||
|
||||
for (s in unknownListNotNull) {
|
||||
println(s.length)
|
||||
}
|
||||
|
||||
for (s in unknownListNullable) {
|
||||
if (s != null) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
for (s in unrelatedListNotNull) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
class Foo {
|
||||
fun test() {
|
||||
val nullableList = J.nullableList<String>()
|
||||
val notNullList = J.notNullList<String>()
|
||||
|
||||
val unknownListNullable = J.unknownList<String>()
|
||||
val unknownListNotNull = J.unknownList<String>()
|
||||
|
||||
val unrelatedListNotNull = J.unrelatedList<Any>()
|
||||
val unrelatedList2 = J.unrelatedList<Any>()
|
||||
|
||||
for (s in notNullList) {
|
||||
println(s.length)
|
||||
}
|
||||
|
||||
for (s in unknownListNotNull) {
|
||||
println(s.length)
|
||||
}
|
||||
|
||||
for (s in unknownListNullable) {
|
||||
if (s != null) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
|
||||
for (s in unrelatedListNotNull) {
|
||||
println(s.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.*;
|
||||
|
||||
class TestWildcard {
|
||||
public ArrayList<? extends Number> extendsNumber = new ArrayList<>();
|
||||
public ArrayList<? super Number> superNumber = new ArrayList<>();
|
||||
|
||||
public void test() {
|
||||
for (Number n : extendsNumber) {
|
||||
System.out.println(n.hashCode());
|
||||
}
|
||||
for (Object o : superNumber) {
|
||||
System.out.println(o.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
internal class TestWildcard {
|
||||
var extendsNumber: ArrayList<out Number> = ArrayList<Number>()
|
||||
var superNumber: ArrayList<in Number> = ArrayList<Number>()
|
||||
|
||||
fun test() {
|
||||
for (n in extendsNumber) {
|
||||
println(n.hashCode())
|
||||
}
|
||||
for (o in superNumber) {
|
||||
println(o.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
internal class TestWildcard {
|
||||
var extendsNumber: ArrayList<out Number> = ArrayList()
|
||||
var superNumber: ArrayList<in Number> = ArrayList()
|
||||
|
||||
fun test() {
|
||||
for (n in extendsNumber) {
|
||||
println(n.hashCode())
|
||||
}
|
||||
for (o in superNumber) {
|
||||
println(o.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package demo
|
||||
|
||||
internal class Test {
|
||||
fun test(vararg args: Any?) {
|
||||
var args = args
|
||||
args = arrayOf<Int?>(1, 2, 3)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@ package demo
|
||||
internal class Test {
|
||||
fun test(vararg args: Any?) {
|
||||
var args = args
|
||||
args = arrayOf<Int?>(1, 2, 3)
|
||||
args = arrayOf<Int>(1, 2, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user