mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-psi] IDEA-377644 PsiImmediateClassType, PsiArrayType: reset nullability when annotations changed
Also: do not merge annotations during the substitution (unnecessary anymore after nullability is implemented and causes problems) Some tests that rely on annotation merging during the substitution are removed/updated. The behavior was inconsistent anyway. GitOrigin-RevId: c13e68616507bbb10cbe0384d122334879e166df
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4f1f7eaf8a
commit
fbd13ff64c
+19
-3
@@ -1,9 +1,8 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.refactoring.changeSignature;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.*;
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
|
||||
import com.intellij.codeInsight.generation.surroundWith.SurroundWithUtil;
|
||||
@@ -936,7 +935,8 @@ public final class JavaChangeSignatureUsageProcessor implements ChangeSignatureU
|
||||
newType = mySubstitutor.substitute(newType);
|
||||
}
|
||||
if (newType != null) {
|
||||
typeElement.replace(myFactory.createTypeElement(newType));
|
||||
PsiTypeElement targetElement = (PsiTypeElement)typeElement.replace(myFactory.createTypeElement(newType));
|
||||
applyNullability(targetElement, newType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -953,6 +953,22 @@ public final class JavaChangeSignatureUsageProcessor implements ChangeSignatureU
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyNullability(@NotNull PsiTypeElement typeElement, @NotNull PsiType origType) {
|
||||
TypeNullability typeNullability = typeElement.getType().getNullability();
|
||||
Nullability nullability = typeNullability.nullability();
|
||||
if (typeNullability.equals(TypeNullability.UNKNOWN)) {
|
||||
PsiElement parent = typeElement.getParent();
|
||||
if (parent instanceof PsiModifierListOwner owner) {
|
||||
nullability = NullableNotNullManager.getNullability(owner);
|
||||
}
|
||||
}
|
||||
if (nullability != origType.getNullability().nullability()) {
|
||||
String annotation = NullableNotNullManager.getInstance(typeElement.getProject())
|
||||
.getDefaultAnnotation(origType.getNullability().nullability(), typeElement);
|
||||
typeElement.addAnnotation(annotation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureNullabilityAnnotationsDoNotRepeat(@NotNull PsiTypeElement element, PsiType newType) {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiModifierListOwner owner &&
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.JavaTypeNullabilityUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an array type.
|
||||
@@ -15,7 +16,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
private final PsiType myComponentType;
|
||||
private final TypeNullability myNullability;
|
||||
private TypeNullability myNullability;
|
||||
|
||||
public PsiArrayType(@NotNull PsiType componentType) {
|
||||
this(componentType, TypeAnnotationProvider.EMPTY);
|
||||
@@ -24,14 +25,14 @@ public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
public PsiArrayType(@NotNull PsiType componentType, PsiAnnotation @NotNull [] annotations) {
|
||||
super(annotations);
|
||||
myComponentType = componentType;
|
||||
myNullability = JavaTypeNullabilityUtil.getNullabilityFromAnnotations(annotations);
|
||||
myNullability = null;
|
||||
}
|
||||
|
||||
public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider) {
|
||||
this(componentType, provider, JavaTypeNullabilityUtil.getNullabilityFromAnnotations(provider.getAnnotations()));
|
||||
this(componentType, provider, null);
|
||||
}
|
||||
|
||||
PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider, @NotNull TypeNullability nullability) {
|
||||
PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider, @Nullable TypeNullability nullability) {
|
||||
super(provider);
|
||||
myComponentType = componentType;
|
||||
myNullability = nullability;
|
||||
@@ -121,6 +122,9 @@ public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
|
||||
@Override
|
||||
public @NotNull TypeNullability getNullability() {
|
||||
if (myNullability == null) {
|
||||
myNullability = JavaTypeNullabilityUtil.getNullabilityFromAnnotations(getAnnotations());
|
||||
}
|
||||
return myNullability;
|
||||
}
|
||||
|
||||
@@ -134,6 +138,15 @@ public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
return new PsiArrayType(getComponentType(), getAnnotationProvider(), nullability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiArrayType annotate(@NotNull TypeAnnotationProvider provider) {
|
||||
PsiArrayType annotated = (PsiArrayType)super.annotate(provider);
|
||||
if (annotated != this) {
|
||||
annotated.myNullability = null;
|
||||
}
|
||||
return annotated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof PsiArrayType &&
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightTypeParameter;
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType;
|
||||
import com.intellij.psi.impl.source.PsiImmediateClassType;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -15,7 +14,6 @@ import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.containers.HashingStrategy;
|
||||
import com.intellij.util.containers.UnmodifiableHashMap;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -145,17 +143,6 @@ public final class PsiSubstitutorImpl implements PsiSubstitutor {
|
||||
return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
|
||||
}
|
||||
|
||||
private static @NotNull TypeAnnotationProvider getMergedProvider(@NotNull PsiType type1, @NotNull PsiType type2) {
|
||||
if(type1.getAnnotationProvider() == TypeAnnotationProvider.EMPTY && !(type1 instanceof PsiClassReferenceType)) {
|
||||
return type2.getAnnotationProvider();
|
||||
}
|
||||
if(type2.getAnnotationProvider() == TypeAnnotationProvider.EMPTY && !(type2 instanceof PsiClassReferenceType)) {
|
||||
return type1.getAnnotationProvider();
|
||||
}
|
||||
return () -> StreamEx.of(type1.getAnnotations()).append(type2.getAnnotations()).distinct(PsiAnnotation::getText)
|
||||
.toArray(PsiAnnotation.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private class SubstitutionVisitor extends PsiTypeMapper {
|
||||
private final boolean ignoreNullity;
|
||||
|
||||
@@ -216,17 +203,13 @@ public final class PsiSubstitutorImpl implements PsiSubstitutor {
|
||||
if (PsiTypes.voidType().equals(result)) {
|
||||
return classType;
|
||||
}
|
||||
if (result != null) {
|
||||
if (result instanceof PsiClassType || result instanceof PsiArrayType || result instanceof PsiWildcardType) {
|
||||
// TODO: remove once nullability works better than annotations
|
||||
result = result.annotate(getMergedProvider(classType, result));
|
||||
}
|
||||
if (!ignoreNullity) {
|
||||
TypeNullability origNullability = classType.getNullability();
|
||||
result = origNullability.equals(TypeNullability.UNKNOWN) ? result : result.withNullability(origNullability.instantiatedWith(result.getNullability()));
|
||||
}
|
||||
if (result == null || ignoreNullity) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
TypeNullability origNullability = classType.getNullability();
|
||||
return origNullability.equals(TypeNullability.UNKNOWN)
|
||||
? result
|
||||
: result.withNullability(origNullability.instantiatedWith(result.getNullability()));
|
||||
}
|
||||
PsiSubstitutor resultSubstitutor = processClass(aClass, resolveResult.getSubstitutor());
|
||||
return new PsiImmediateClassType(aClass, resultSubstitutor, classType.getLanguageLevel(),
|
||||
|
||||
@@ -120,7 +120,9 @@ public class PsiClassReferenceType extends PsiClassType.Stub {
|
||||
@Override
|
||||
public @NotNull PsiClassType annotate(@NotNull TypeAnnotationProvider provider) {
|
||||
PsiClassReferenceType annotated = (PsiClassReferenceType)super.annotate(provider);
|
||||
annotated.myNullability = null;
|
||||
if (annotated != this) {
|
||||
annotated.myNullability = null;
|
||||
}
|
||||
return annotated;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,15 @@ public class PsiImmediateClassType extends PsiClassType.Stub {
|
||||
return new PsiImmediateClassType(myClass, mySubstitutor, myLanguageLevel, getAnnotationProvider(), myPsiContext, nullability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiImmediateClassType annotate(@NotNull TypeAnnotationProvider provider) {
|
||||
PsiImmediateClassType annotated = (PsiImmediateClassType)super.annotate(provider);
|
||||
if (annotated != this) {
|
||||
annotated.myNullability = null;
|
||||
}
|
||||
return annotated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterCount() {
|
||||
PsiTypeParameterList list = myClass.getTypeParameterList();
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ interface Child extends Parent<@NotNull String> {
|
||||
|
||||
class JavaMain {
|
||||
void test(Child child) {
|
||||
for (@Anno String s : child.getList()) {
|
||||
for (String s : child.getList()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-21
@@ -20,7 +20,6 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
@@ -195,26 +194,6 @@ public class LightAdvHighlightingFixtureTest extends LightJavaCodeInsightFixture
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testTypeAnnotations() {
|
||||
myFixture.addClass("""
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
@Target({ElementType.TYPE_USE})
|
||||
@interface Nullable {}
|
||||
""");
|
||||
myFixture.addClass("class Middle<R> extends Base<@Nullable R, String>{}");
|
||||
myFixture.addClass("class Child<R> extends Middle<R>{}");
|
||||
PsiClass baseClass = myFixture.addClass("class Base<R, C> {}");
|
||||
PsiClass fooClass = myFixture.addClass("""
|
||||
class Foo {
|
||||
Child<String> field;
|
||||
}""");
|
||||
PsiField fooField = fooClass.findFieldByName("field", false);
|
||||
PsiType substituted =
|
||||
TypeConversionUtil.getSuperClassSubstitutor(baseClass, (PsiClassType)fooField.getType()).substitute(baseClass.getTypeParameters()[0]);
|
||||
assertEquals(1, substituted.getAnnotations().length);
|
||||
}
|
||||
|
||||
public void testCodeFragmentMayAccessDefaultPackage() {
|
||||
myFixture.addClass("public class MainClass { }");
|
||||
|
||||
|
||||
@@ -34,12 +34,9 @@ class NlsTypeUse {
|
||||
cons.accept(<warning descr="Hardcoded string literal: \"foo\"">"foo"</warning>, "bar");
|
||||
}
|
||||
|
||||
void typeArgsCustomType(NlsValueMap<String> map, NlsValueMap2<String, String> map2) {
|
||||
void typeArgsCustomType(NlsValueMap<String> map) {
|
||||
map.put("foo", <warning descr="Hardcoded string literal: \"bar\"">"bar"</warning>);
|
||||
map2.put("foo", <warning descr="Hardcoded string literal: \"bar\"">"bar"</warning>);
|
||||
}
|
||||
|
||||
interface NlsValueMap<K> extends Map<K, @Nls String> {}
|
||||
|
||||
interface NlsValueMap2<K, V> extends Map<K, @Nls V> {}
|
||||
}
|
||||
Reference in New Issue
Block a user