mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-inspections] IDEA-380526 Jspecify. False positive. Non-null type argument is expected for Array type
GitOrigin-RevId: 132eab2bc3bc9c4f6995a29796d8596b6e4a3c5c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ad6d52df3b
commit
8c3b69c65b
@@ -475,8 +475,11 @@ public final class GenericsUtil {
|
||||
}
|
||||
}
|
||||
else if (type instanceof PsiArrayType) {
|
||||
PsiType component = eliminateWildcards(((PsiArrayType)type).getComponentType(), false);
|
||||
PsiType newArray = type instanceof PsiEllipsisType ? new PsiEllipsisType(component) : new PsiArrayType(component);
|
||||
PsiArrayType psiArrayType = (PsiArrayType)type;
|
||||
PsiType component = eliminateWildcards(psiArrayType.getComponentType(), false);
|
||||
PsiType newArray = type instanceof PsiEllipsisType ?
|
||||
new PsiEllipsisType(component).withContainerNullability(psiArrayType) :
|
||||
new PsiArrayType(component).withContainerNullability(psiArrayType);
|
||||
return newArray.annotate(type.getAnnotationProvider()).withNullability(type.getNullability());
|
||||
}
|
||||
else if (type instanceof PsiWildcardType) {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.codeInsight.NullabilityAnnotationInfo;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.TypeNullability;
|
||||
import com.intellij.lang.jvm.types.JvmArrayType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.JavaTypeNullabilityUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -16,26 +20,30 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
private final PsiType myComponentType;
|
||||
private TypeNullability myNullability;
|
||||
TypeNullability myNullability;
|
||||
@Nullable
|
||||
final PsiTypeElementPointer myElementPointer;
|
||||
|
||||
public PsiArrayType(@NotNull PsiType componentType) {
|
||||
this(componentType, TypeAnnotationProvider.EMPTY);
|
||||
}
|
||||
|
||||
public PsiArrayType(@NotNull PsiType componentType, PsiAnnotation @NotNull [] annotations) {
|
||||
super(annotations);
|
||||
myComponentType = componentType;
|
||||
myNullability = null;
|
||||
this(componentType, TypeAnnotationProvider.Static.create(annotations));
|
||||
}
|
||||
|
||||
public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider) {
|
||||
this(componentType, provider, null);
|
||||
this(componentType, provider, null, null);
|
||||
}
|
||||
|
||||
PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider, @Nullable TypeNullability nullability) {
|
||||
PsiArrayType(@NotNull PsiType componentType,
|
||||
@NotNull TypeAnnotationProvider provider,
|
||||
@Nullable TypeNullability nullability,
|
||||
@Nullable PsiTypeElementPointer elememtPointer) {
|
||||
super(provider);
|
||||
myComponentType = componentType;
|
||||
myNullability = nullability;
|
||||
myElementPointer = elememtPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,10 +128,60 @@ public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
return myComponentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array type instance with the specified nullable container nullability.
|
||||
*
|
||||
* @param elementPointer the PSI element pointer representing the context, or null if no specific context is required.
|
||||
* @return a new instance of {@link PsiType} with the specified nullable container nullability.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NotNull
|
||||
public PsiType withContainerNullability(@Nullable PsiTypeElementPointer elementPointer) {
|
||||
if (elementPointer == null) return this;
|
||||
if (elementPointer == myElementPointer) return this;
|
||||
return new PsiArrayType(myComponentType, getAnnotationProvider(), myNullability, elementPointer);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Nullable
|
||||
static TypeNullability getContainerNullability(@NotNull PsiTypeElement psiContext) {
|
||||
Project project = psiContext.getProject();
|
||||
if (project.isDefault()) return null;
|
||||
NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
|
||||
if (manager == null) return null;
|
||||
NullabilityAnnotationInfo defaultNullability = manager.findDefaultTypeUseNullability(psiContext);
|
||||
TypeNullability nullability = defaultNullability == null ? null : defaultNullability.toTypeNullability();
|
||||
return nullability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array type instance with the specified nullable container nullability.
|
||||
*
|
||||
* @param arrayType the array type from which container nullability will be taken.
|
||||
* @return a new instance of {@link PsiType} with the specified nullable container nullability.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NotNull
|
||||
public PsiType withContainerNullability(@Nullable PsiArrayType arrayType) {
|
||||
if (arrayType == null) return this;
|
||||
if (arrayType.myElementPointer == myElementPointer) return this;
|
||||
return new PsiArrayType(myComponentType, getAnnotationProvider(), myNullability, arrayType.myElementPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TypeNullability getNullability() {
|
||||
if (myNullability == null) {
|
||||
myNullability = JavaTypeNullabilityUtil.getNullabilityFromAnnotations(getAnnotations());
|
||||
TypeNullability nullability = JavaTypeNullabilityUtil.getNullabilityFromAnnotations(getAnnotations());
|
||||
if (nullability == TypeNullability.UNKNOWN && myElementPointer != null) {
|
||||
PsiTypeElement element = myElementPointer.retrieveElement();
|
||||
if (element != null) {
|
||||
TypeNullability containerNullability = getContainerNullability(element);
|
||||
if (containerNullability != null) {
|
||||
nullability = containerNullability;
|
||||
}
|
||||
}
|
||||
}
|
||||
myNullability = nullability;
|
||||
}
|
||||
return myNullability;
|
||||
}
|
||||
@@ -135,7 +193,7 @@ public class PsiArrayType extends PsiType.Stub implements JvmArrayType {
|
||||
*/
|
||||
@Override
|
||||
public @NotNull PsiArrayType withNullability(@NotNull TypeNullability nullability) {
|
||||
return new PsiArrayType(getComponentType(), getAnnotationProvider(), nullability);
|
||||
return new PsiArrayType(getComponentType(), getAnnotationProvider(), nullability, myElementPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.psi;
|
||||
import com.intellij.codeInsight.TypeNullability;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the type of a variable arguments array passed as a method parameter.
|
||||
@@ -20,9 +21,12 @@ public class PsiEllipsisType extends PsiArrayType {
|
||||
public PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider) {
|
||||
super(componentType, provider);
|
||||
}
|
||||
|
||||
private PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider provider, @NotNull TypeNullability nullability) {
|
||||
super(componentType, provider, nullability);
|
||||
|
||||
private PsiEllipsisType(@NotNull PsiType componentType,
|
||||
@NotNull TypeAnnotationProvider provider,
|
||||
@Nullable TypeNullability nullability,
|
||||
@Nullable PsiTypeElementPointer typeElementPointer) {
|
||||
super(componentType, provider, nullability, typeElementPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,9 +50,25 @@ public class PsiEllipsisType extends PsiArrayType {
|
||||
super.equalsToText(text);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiType withContainerNullability(@Nullable PsiTypeElementPointer elementPointer) {
|
||||
if (elementPointer == null) return this;
|
||||
if (elementPointer == myElementPointer) return this;
|
||||
return new PsiEllipsisType(getComponentType(), getAnnotationProvider(), myNullability, elementPointer);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiType withContainerNullability(@Nullable PsiArrayType arrayType) {
|
||||
if (arrayType == null) return this;
|
||||
if (arrayType.myElementPointer == myElementPointer) return this;
|
||||
return new PsiEllipsisType(getComponentType(), getAnnotationProvider(), myNullability, arrayType.myElementPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiEllipsisType withNullability(@NotNull TypeNullability nullability) {
|
||||
return new PsiEllipsisType(getComponentType(), getAnnotationProvider(), nullability);
|
||||
return new PsiEllipsisType(getComponentType(), getAnnotationProvider(), nullability, this.myElementPointer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.psi;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public interface PsiTypeElementPointer {
|
||||
@Nullable
|
||||
PsiTypeElement retrieveElement();
|
||||
|
||||
|
||||
static PsiTypeElementPointer constant(@NotNull PsiTypeElement ref) {
|
||||
return new PsiTypeElementPointer() {
|
||||
@Override
|
||||
public @NotNull PsiTypeElement retrieveElement() {
|
||||
return ref;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,8 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx<PsiType> {
|
||||
PsiType mappedComponent = mapType(componentType);
|
||||
if (mappedComponent == null) return null;
|
||||
if (mappedComponent == componentType) return type;
|
||||
return new PsiArrayType(mappedComponent, type.getAnnotationProvider());
|
||||
return new PsiArrayType(mappedComponent, type.getAnnotationProvider())
|
||||
.withContainerNullability(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -189,9 +189,9 @@ public class ClsTypeElementImpl extends ClsElementImpl implements PsiTypeElement
|
||||
PsiType type = deepestChild.getType();
|
||||
for (int i = depth - 1; i >= 0; i--) {
|
||||
if (i == 0 && isVarArgs()) {
|
||||
type = new PsiEllipsisType(type);
|
||||
type = new PsiEllipsisType(type).withContainerNullability(PsiTypeElementPointer.constant(deepestChild));
|
||||
} else {
|
||||
type = type.createArrayType();
|
||||
type = type.createArrayType().withContainerNullability(PsiTypeElementPointer.constant(deepestChild));
|
||||
}
|
||||
type = type.annotate(containers.get(i).getProvider(this));
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@ package com.intellij.psi.impl.source;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.RecursionGuard;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.PsiJavaParserFacadeImpl;
|
||||
import com.intellij.psi.impl.compiled.ClsTypeElementImpl;
|
||||
import com.intellij.psi.impl.source.tree.*;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -26,6 +29,9 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeElement {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(PsiTypeElementImpl.class);
|
||||
|
||||
public PsiTypeElementImpl() {
|
||||
this(JavaElementType.TYPE);
|
||||
}
|
||||
@@ -84,7 +90,8 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
else if (PsiUtil.isJavaToken(child, ElementType.PRIMITIVE_TYPE_BIT_SET)) {
|
||||
assert type == null : this;
|
||||
String text = child.getText();
|
||||
type = annotations.isEmpty() ? PsiJavaParserFacadeImpl.getPrimitiveType(text) : new PsiPrimitiveType(text, createProvider(annotations));
|
||||
type =
|
||||
annotations.isEmpty() ? PsiJavaParserFacadeImpl.getPrimitiveType(text) : new PsiPrimitiveType(text, createProvider(annotations));
|
||||
}
|
||||
else if (PsiUtil.isJavaToken(child, JavaTokenType.VAR_KEYWORD)) {
|
||||
assert type == null : this;
|
||||
@@ -153,12 +160,14 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private static PsiType createArray(PsiType elementType, List<TypeAnnotationProvider> providers, boolean ellipsis) {
|
||||
|
||||
private PsiType createArray(PsiType elementType, List<TypeAnnotationProvider> providers, boolean ellipsis) {
|
||||
PsiType result = elementType;
|
||||
for (int i = providers.size() - 1; i >= 0; i--) {
|
||||
TypeAnnotationProvider provider = providers.get(i);
|
||||
result = ellipsis && i == 0 ? new PsiEllipsisType(result, provider) : new PsiArrayType(result, provider);
|
||||
result = ellipsis && i == 0 ?
|
||||
new PsiEllipsisType(result, provider).withContainerNullability(createTypeElementPointer(this)) :
|
||||
new PsiArrayType(result, provider).withContainerNullability(createTypeElementPointer(this));
|
||||
}
|
||||
providers.clear();
|
||||
return result;
|
||||
@@ -212,15 +221,11 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
@Override
|
||||
public boolean isInferredType() {
|
||||
return PsiUtil.isJavaToken(getFirstChild(), JavaTokenType.VAR_KEYWORD) ||
|
||||
PsiAugmentProvider.isInferredType(this);
|
||||
PsiAugmentProvider.isInferredType(this);
|
||||
}
|
||||
|
||||
private static @NotNull ClassReferencePointer getReferenceComputable(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
PsiElement root = SyntaxTraverser.psiApi()
|
||||
.parents(ref.getParent())
|
||||
.takeWhile(it -> it instanceof PsiTypeElement || it instanceof PsiReferenceParameterList || it instanceof PsiJavaCodeReferenceElement)
|
||||
.last();
|
||||
PsiElement parent = root instanceof PsiTypeElement ? root.getParent() : null;
|
||||
PsiElement parent = getAnchorParent(ref);
|
||||
if (parent instanceof PsiMethod || parent instanceof PsiVariable) {
|
||||
PsiModifierListOwner owner = (PsiModifierListOwner)parent;
|
||||
int[] pathFromRoot = getPathFromRoot(ref);
|
||||
@@ -228,12 +233,21 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
}
|
||||
return ClassReferencePointer.constant(ref);
|
||||
}
|
||||
|
||||
|
||||
private static @Nullable PsiElement getAnchorParent(@NotNull PsiElement ref) {
|
||||
PsiElement root = SyntaxTraverser.psiApi()
|
||||
.parents(ref)
|
||||
.takeWhile(it -> it instanceof PsiTypeElement || it instanceof PsiReferenceParameterList || it instanceof PsiJavaCodeReferenceElement)
|
||||
.last();
|
||||
PsiElement parent = root instanceof PsiTypeElement ? root.getParent() : null;
|
||||
return parent;
|
||||
}
|
||||
|
||||
// n = -1 => go to qualifier
|
||||
// n >= 0 => go to type parameter #n
|
||||
private static int[] getPathFromRoot(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
IntList result = null;
|
||||
while(true) {
|
||||
while (true) {
|
||||
PsiElement parent = ref.getParent();
|
||||
if (parent instanceof PsiJavaCodeReferenceElement) {
|
||||
PsiJavaCodeReferenceElement parentRef = (PsiJavaCodeReferenceElement)parent;
|
||||
@@ -241,10 +255,12 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
if (result == null) result = new IntArrayList();
|
||||
result.add(0, -1);
|
||||
ref = parentRef;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unexpected parent (going not from qualifier): " + parent.getText());
|
||||
}
|
||||
} else if (parent instanceof PsiTypeElement) {
|
||||
}
|
||||
else if (parent instanceof PsiTypeElement) {
|
||||
PsiElement nextParent = parent.getParent();
|
||||
while (nextParent instanceof PsiTypeElement) {
|
||||
parent = nextParent;
|
||||
@@ -260,10 +276,12 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
ref = (PsiJavaCodeReferenceElement)nextRef;
|
||||
if (result == null) result = new IntArrayList();
|
||||
result.add(0, index);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return result == null ? ArrayUtil.EMPTY_INT_ARRAY : result.toIntArray();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unexpected parent: " + parent.getText());
|
||||
}
|
||||
}
|
||||
@@ -442,7 +460,8 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
if (parent instanceof PsiModifierListOwner) {
|
||||
PsiModifierList modifierList = ((PsiModifierListOwner)parent).getModifierList();
|
||||
if (modifierList != null) {
|
||||
PsiTypeParameterList list = parent instanceof PsiTypeParameterListOwner ? ((PsiTypeParameterListOwner)parent).getTypeParameterList() : null;
|
||||
PsiTypeParameterList list =
|
||||
parent instanceof PsiTypeParameterListOwner ? ((PsiTypeParameterListOwner)parent).getTypeParameterList() : null;
|
||||
if (list == null || list.textMatches("")) {
|
||||
return (PsiAnnotation)modifierList.add(annotation);
|
||||
}
|
||||
@@ -498,4 +517,84 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
|
||||
public String toString() {
|
||||
return "PsiTypeElement:" + getText();
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@NotNull
|
||||
public static PsiTypeElementPointer createTypeElementPointer(@NotNull PsiTypeElement typeElement) {
|
||||
if (typeElement instanceof ClsTypeElementImpl) {
|
||||
return PsiTypeElementPointer.constant(typeElement);
|
||||
}
|
||||
PsiElement parent = getAnchorParent(typeElement);
|
||||
if ((parent instanceof JavaStubPsiElement) && (parent instanceof PsiMethod || parent instanceof PsiVariable)) {
|
||||
PsiModifierListOwner owner = (PsiModifierListOwner)parent;
|
||||
PsiTypeElement ownerType =
|
||||
parent instanceof PsiMethod ? ((PsiMethod)parent).getReturnTypeElement() : ((PsiVariable)parent).getTypeElement();
|
||||
if (ownerType == null) return PsiTypeElementPointer.constant(typeElement);
|
||||
int[] pathFromRoot = getPathFromRoot(ownerType, typeElement);
|
||||
if (pathFromRoot == null) return PsiTypeElementPointer.constant(typeElement);
|
||||
return computeTypeElementPointerFromTypeOwner(owner, pathFromRoot, new WeakReference<>(typeElement));
|
||||
}
|
||||
return PsiTypeElementPointer.constant(typeElement);
|
||||
}
|
||||
|
||||
private static @NotNull PsiTypeElementPointer computeTypeElementPointerFromTypeOwner(@NotNull PsiModifierListOwner owner,
|
||||
int @NotNull [] root,
|
||||
@NotNull WeakReference<PsiTypeElement> reference) {
|
||||
return new PsiTypeElementPointer() {
|
||||
|
||||
@Override
|
||||
public @Nullable PsiTypeElement retrieveElement() {
|
||||
PsiTypeElement element = reference.get();
|
||||
if (element != null && element.isValid()) return element;
|
||||
if (!owner.isValid()) return null;
|
||||
PsiElement parentTypeElement =
|
||||
owner instanceof PsiMethod ? ((PsiMethod)owner).getReturnTypeElement() : ((PsiVariable)owner).getTypeElement();
|
||||
if (parentTypeElement == null) return null;
|
||||
PsiElement typeElement = parentTypeElement;
|
||||
for (int i : root) {
|
||||
List<@NotNull PsiElement> filtered = ContainerUtil.filter(typeElement.getChildren(),
|
||||
t -> t instanceof PsiTypeElement ||
|
||||
t instanceof PsiJavaCodeReferenceElement ||
|
||||
t instanceof PsiReferenceParameterList);
|
||||
if (i >= filtered.size()) return null;
|
||||
typeElement = filtered.get(i);
|
||||
}
|
||||
if (typeElement instanceof PsiTypeElement) {
|
||||
return (PsiTypeElement)typeElement;
|
||||
}
|
||||
|
||||
LOG.error("Can't decode PsiTypeElement with indexes",
|
||||
new Attachment("memberType.txt", parentTypeElement.getText() + "\n" + Arrays.toString(root)));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int @Nullable [] getPathFromRoot(@NotNull PsiTypeElement targetParent, @NotNull PsiTypeElement child) {
|
||||
IntList result = new IntArrayList();
|
||||
PsiElement currentChild = child;
|
||||
while (true) {
|
||||
if (currentChild == targetParent) {
|
||||
return result.toIntArray();
|
||||
}
|
||||
PsiElement parent = currentChild.getParent();
|
||||
if (parent instanceof PsiTypeElement ||
|
||||
parent instanceof PsiJavaCodeReferenceElement ||
|
||||
parent instanceof PsiReferenceParameterList) {
|
||||
List<PsiElement> psiTypeElementChildren =
|
||||
ContainerUtil.filter(parent.getChildren(),
|
||||
t -> t instanceof PsiTypeElement ||
|
||||
t instanceof PsiJavaCodeReferenceElement ||
|
||||
t instanceof PsiReferenceParameterList);
|
||||
int index = psiTypeElementChildren.indexOf(currentChild);
|
||||
result.add(0, index);
|
||||
currentChild = parent;
|
||||
continue;
|
||||
}
|
||||
LOG.error("Can't code PsiTypeElement with indexes",
|
||||
new Attachment("memberType.txt", targetParent.getText() + "\n" + child.getText()));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.GeneratedMarkerVisitor;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.cache.TypeInfo;
|
||||
import com.intellij.psi.impl.source.PsiTypeElementImpl;
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.AnnotationElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -43,7 +44,9 @@ public final class JavaSharedImplUtil {
|
||||
List<PsiAnnotation[]> allAnnotations = collectAnnotations(anchor, stopAt);
|
||||
if (allAnnotations == null) return null;
|
||||
for (int i = 0, size = allAnnotations.size(); i < size; i++) {
|
||||
type = ((ellipsisType && i == size - 1) ? new PsiEllipsisType(type) : type.createArrayType())
|
||||
type = ((ellipsisType && i == size - 1) ?
|
||||
new PsiEllipsisType(type).withContainerNullability(PsiTypeElementImpl.createTypeElementPointer(typeElement) ) :
|
||||
type.createArrayType().withContainerNullability(PsiTypeElementImpl.createTypeElementPointer(typeElement)))
|
||||
.annotate(TypeAnnotationProvider.Static.create(allAnnotations.get(i)));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.example;
|
||||
|
||||
import org.jspecify.annotations.*;
|
||||
|
||||
class UseArrayTest {
|
||||
|
||||
@NullMarked
|
||||
abstract static class Container {
|
||||
interface Lib<T extends @Nullable Object> {
|
||||
T get();
|
||||
}
|
||||
|
||||
abstract Lib<String[][]> useArray1();
|
||||
|
||||
abstract Lib<? extends String[][]> useArray2();
|
||||
}
|
||||
|
||||
|
||||
@NullMarked
|
||||
abstract class Container2<L> {
|
||||
interface Lib2<T> {
|
||||
T get();
|
||||
}
|
||||
|
||||
abstract Lib2<L> useArray2();
|
||||
}
|
||||
|
||||
class ArrayUseTest {
|
||||
void test(Container a, Container2<? extends String[][]> b) {
|
||||
if (<warning descr="Condition 'a.useArray1().get() == null' is always 'false'">a.useArray1().get() == null</warning>) { //expected
|
||||
System.out.println("1");
|
||||
}
|
||||
|
||||
String[][] strings2 = a.useArray2().get();
|
||||
if (<warning descr="Condition 'strings2 == null' is always 'false'">strings2 == null</warning>) { //expected
|
||||
System.out.println("1");
|
||||
}
|
||||
String[][] string3 = b.useArray2().get();
|
||||
if (<warning descr="Condition 'string3 == null' is always 'false'">string3 == null</warning>) { //expected
|
||||
System.out.println("1");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.example;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
class SimpleArrayTest {
|
||||
static class Container {
|
||||
@NullMarked
|
||||
abstract static class ArraySameType {
|
||||
interface Lib<T> {
|
||||
T get();
|
||||
}
|
||||
|
||||
abstract void useArray(Lib<Object[]> l); //not expected
|
||||
|
||||
abstract Lib<? extends String[][]> useArray2(); //not expected
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -249,4 +249,9 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testJSpecifyArrayNullability() {
|
||||
addJSpecifyNullMarked(myFixture);
|
||||
setupTypeUseAnnotations("org.jspecify.annotations", myFixture);
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
+6
@@ -514,4 +514,10 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
|
||||
setupTypeUseAnnotations("org.jspecify.annotations", myFixture);
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testArrayUnderContainerAnnotation() {
|
||||
myInspection.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE = true;
|
||||
addJSpecifyNullMarked(myFixture);
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.psi;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType;
|
||||
@@ -11,6 +12,7 @@ import com.intellij.psi.impl.source.tree.java.ParameterElement;
|
||||
import com.intellij.testFramework.LeakHunter;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.intellij.util.ref.GCWatcher;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
public class AstLeaksTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public void test_AST_should_be_on_a_soft_reference__for_changed_files_as_well() {
|
||||
@@ -90,6 +92,134 @@ public class AstLeaksTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
assertFalse(file.isContentsLoaded());
|
||||
}
|
||||
|
||||
public void testNullabilityCorrectLoad() {
|
||||
@Language("JAVA") String nullMarked =
|
||||
"""
|
||||
package org.jspecify.annotations;
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.MODULE})
|
||||
public @interface NullMarked {}""";
|
||||
myFixture.addClass(nullMarked);
|
||||
final PsiClass cls = myFixture.addClass(
|
||||
"""
|
||||
@org.jspecify.annotations.NullMarked
|
||||
class Foo {
|
||||
static void test(java.util.Map<String, ? extends java.lang.String[]> args) {}
|
||||
}""");
|
||||
PsiFileImpl file = (PsiFileImpl)cls.getContainingFile();
|
||||
assertNotNull(cls.getNode());
|
||||
PsiType type = cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getType();
|
||||
assertTrue(type instanceof PsiClassReferenceType);
|
||||
|
||||
LeakHunter.checkLeak(type, ParameterElement.class,
|
||||
node -> node.getPsi().equals(cls.getMethods()[0].getParameterList().getParameters()[0]));
|
||||
|
||||
GCWatcher.tracking(cls.getNode()).ensureCollected();
|
||||
assertFalse(file.isContentsLoaded());
|
||||
|
||||
assertTrue(type.equalsToText("java.util.Map<java.lang.String,? extends java.lang.String[]>"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
PsiType targetType = ((PsiWildcardType)((PsiClassReferenceType)(type)).getParameters()[1]).getBound();
|
||||
assertTrue(targetType instanceof PsiArrayType);
|
||||
assertTrue(targetType.equalsToText("java.lang.String[]"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertSame(targetType.getNullability().nullability(), Nullability.NOT_NULL);
|
||||
}
|
||||
|
||||
public void testNullabilityCorrectLoadGetBeforeAsymPath() {
|
||||
@Language("JAVA") String nullMarked =
|
||||
"""
|
||||
package org.jspecify.annotations;
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.MODULE})
|
||||
public @interface NullMarked {}""";
|
||||
myFixture.addClass(nullMarked);
|
||||
final PsiClass cls = myFixture.addClass(
|
||||
"""
|
||||
@org.jspecify.annotations.NullMarked
|
||||
class Foo {
|
||||
static void test(java.util.Map<String, java.lang.String[]> args) {}
|
||||
}""");
|
||||
PsiFileImpl file = (PsiFileImpl)cls.getContainingFile();
|
||||
assertNotNull(cls.getNode());
|
||||
PsiType type = cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getType();
|
||||
assertTrue(type instanceof PsiClassReferenceType);
|
||||
//don't create intermediate variables, it is necessary to skip it for gc collection
|
||||
PsiType targetType = ((PsiTypeElement)cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getChildren()[0].getChildren()[3].getChildren()[4]).getType();
|
||||
LeakHunter.checkLeak(targetType, ParameterElement.class,
|
||||
node -> node.getPsi().equals(cls.getMethods()[0].getParameterList().getParameters()[0]));
|
||||
GCWatcher.tracking(cls.getNode()).ensureCollected();
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertTrue(type.equalsToText("java.util.Map<java.lang.String,java.lang.String[]>"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertTrue(targetType instanceof PsiArrayType);
|
||||
assertTrue(targetType.equalsToText("java.lang.String[]"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertSame(targetType.getNullability().nullability(), Nullability.NOT_NULL);
|
||||
}
|
||||
|
||||
public void testNullabilityCorrectLoadGetBefore() {
|
||||
@Language("JAVA") String nullMarked =
|
||||
"""
|
||||
package org.jspecify.annotations;
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.MODULE})
|
||||
public @interface NullMarked {}""";
|
||||
myFixture.addClass(nullMarked);
|
||||
final PsiClass cls = myFixture.addClass(
|
||||
"""
|
||||
@org.jspecify.annotations.NullMarked
|
||||
class Foo {
|
||||
static void test(java.util.Map<String, ? extends java.lang.String[]> args) {}
|
||||
}""");
|
||||
PsiFileImpl file = (PsiFileImpl)cls.getContainingFile();
|
||||
assertNotNull(cls.getNode());
|
||||
PsiType type = cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getType();
|
||||
assertTrue(type instanceof PsiClassReferenceType);
|
||||
//don't create an intermediate variables, it is necessary to skip it for gc collection
|
||||
PsiType targetType = ((PsiTypeElement)cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getChildren()[0].getChildren()[3].getChildren()[4].getChildren()[4]).getType();
|
||||
LeakHunter.checkLeak(targetType, ParameterElement.class,
|
||||
node -> node.getPsi().equals(cls.getMethods()[0].getParameterList().getParameters()[0]));
|
||||
GCWatcher.tracking(cls.getNode()).ensureCollected();
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertTrue(type.equalsToText("java.util.Map<java.lang.String,? extends java.lang.String[]>"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertTrue(targetType instanceof PsiArrayType);
|
||||
assertTrue(targetType.equalsToText("java.lang.String[]"));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertSame(targetType.getNullability().nullability(), Nullability.NOT_NULL);
|
||||
}
|
||||
|
||||
public void testNullabilityCorrectLoadEllipsis() {
|
||||
@Language("JAVA") String nullMarked =
|
||||
"""
|
||||
package org.jspecify.annotations;
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.MODULE})
|
||||
public @interface NullMarked {}""";
|
||||
myFixture.addClass(nullMarked);
|
||||
final PsiClass cls = myFixture.addClass(
|
||||
"""
|
||||
@org.jspecify.annotations.NullMarked
|
||||
class Foo {
|
||||
static void test(String... args) {}
|
||||
}""");
|
||||
PsiFileImpl file = (PsiFileImpl)cls.getContainingFile();
|
||||
assertNotNull(cls.getNode());
|
||||
PsiType type = cls.getMethods()[0].getParameterList().getParameters()[0].getTypeElement().getType();
|
||||
assertTrue(type instanceof PsiEllipsisType);
|
||||
|
||||
LeakHunter.checkLeak(type, ParameterElement.class,
|
||||
node -> node.getPsi().equals(cls.getMethods()[0].getParameterList().getParameters()[0]));
|
||||
|
||||
GCWatcher.tracking(cls.getNode()).ensureCollected();
|
||||
assertFalse(file.isContentsLoaded());
|
||||
|
||||
assertTrue(type.equalsToText("java.lang.String..."));
|
||||
assertFalse(file.isContentsLoaded());
|
||||
assertSame(type.getNullability().nullability(), Nullability.NOT_NULL);
|
||||
}
|
||||
|
||||
public void test_no_hard_refs_to_AST_via_array_component_type() {
|
||||
final PsiClass cls = myFixture.addClass("class Foo { Object[] bar() {} }");
|
||||
PsiFileImpl file = (PsiFileImpl)cls.getContainingFile();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.psi;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.TypeNullability;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
@@ -555,4 +557,11 @@ public class ClsPsiTest extends LightIdeaTestCase {
|
||||
assertThat(PsiInvalidElementAccessException.findOutInvalidationReason(mirror)).contains(
|
||||
PsiInvalidElementAccessException.findOutInvalidationReason(clsFile));
|
||||
}
|
||||
|
||||
public void testClsArrayTypeCreation() {
|
||||
PsiJavaFile file = getFile("ClsContainer");
|
||||
PsiType type = file.getClasses()[0].getMethods()[3].getReturnType();
|
||||
TypeNullability nullability = type.getNullability();
|
||||
assertEquals(Nullability.NOT_NULL, nullability.nullability());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user