mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] Support annotation nicknames in getAnnotationNullability()
Fixes IDEA-372080 Invalid null check for array with @Nullable Also: do not attempt to fill nicknames in dumb mode GitOrigin-RevId: 7955160a958d9891d382ef2736d4c72fe926dbbf
This commit is contained in:
committed by
intellij-monorepo-bot
parent
45b2d9b242
commit
74800bc0b5
@@ -17,6 +17,7 @@ import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.extensions.ExtensionPointListener;
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
@@ -142,16 +143,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<Nullability> getAnnotationNullability(String name) {
|
||||
if (myNotNulls.contains(name)) {
|
||||
return Optional.of(Nullability.NOT_NULL);
|
||||
}
|
||||
if (myNullables.contains(name)) {
|
||||
return Optional.of(Nullability.NULLABLE);
|
||||
}
|
||||
if (myDefaultUnknowns.containsKey(name)) {
|
||||
return Optional.of(Nullability.UNKNOWN);
|
||||
}
|
||||
return Optional.empty();
|
||||
return Optional.ofNullable(getAllNullabilityAnnotationsWithNickNames().getNullability(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -361,41 +353,40 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
|
||||
|
||||
@Override
|
||||
protected @NotNull NullabilityAnnotationDataHolder getAllNullabilityAnnotationsWithNickNames() {
|
||||
if (DumbService.isDumb(myProject)) {
|
||||
// Searching for nullability nicknames is not available in the dumb mode
|
||||
return NullabilityAnnotationDataHolder.fromMap(getNullabilityMap());
|
||||
}
|
||||
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> {
|
||||
Map<String, Nullability> result = new HashMap<>();
|
||||
for (String qName : myDefaultAll) {
|
||||
result.put(qName, null);
|
||||
}
|
||||
for (String qName : getNotNulls()) {
|
||||
result.put(qName, Nullability.NOT_NULL);
|
||||
}
|
||||
for (String qName : getNullables()) {
|
||||
result.put(qName, Nullability.NULLABLE);
|
||||
}
|
||||
for (String qName : myDefaultUnknowns.keySet()) {
|
||||
result.put(qName, Nullability.UNKNOWN);
|
||||
}
|
||||
Map<String, Nullability> result = getNullabilityMap();
|
||||
for (PsiClass aClass : getAllNullabilityNickNames()) {
|
||||
String qName = aClass.getQualifiedName();
|
||||
if (qName != null) {
|
||||
result.putIfAbsent(qName, Jsr305Support.getNickNamedNullability(aClass));
|
||||
}
|
||||
}
|
||||
NullabilityAnnotationDataHolder holder = new NullabilityAnnotationDataHolder() {
|
||||
@Override
|
||||
public Set<String> qualifiedNames() {
|
||||
return result.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Nullability getNullability(String annotation) {
|
||||
return result.get(annotation);
|
||||
}
|
||||
};
|
||||
NullabilityAnnotationDataHolder holder = NullabilityAnnotationDataHolder.fromMap(result);
|
||||
return Result.create(holder, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
});
|
||||
}
|
||||
|
||||
private @NotNull Map<String, Nullability> getNullabilityMap() {
|
||||
Map<String, Nullability> result = new HashMap<>();
|
||||
for (String qName : myDefaultAll) {
|
||||
result.put(qName, null);
|
||||
}
|
||||
for (String qName : getNotNulls()) {
|
||||
result.put(qName, Nullability.NOT_NULL);
|
||||
}
|
||||
for (String qName : getNullables()) {
|
||||
result.put(qName, Nullability.NULLABLE);
|
||||
}
|
||||
for (String qName : myDefaultUnknowns.keySet()) {
|
||||
result.put(qName, Nullability.UNKNOWN);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Nullability correctNullability(@NotNull Nullability nullability, @NotNull PsiAnnotation annotation) {
|
||||
if (nullability == Nullability.NOT_NULL && annotation.hasQualifiedName(Jsr305Support.JAVAX_ANNOTATION_NONNULL)) {
|
||||
|
||||
@@ -10,10 +10,7 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.codeInsight.AnnotationUtil.*;
|
||||
@@ -489,5 +486,23 @@ public abstract class NullableNotNullManager {
|
||||
* @return nullability
|
||||
*/
|
||||
@Nullable Nullability getNullability(String annotation);
|
||||
|
||||
/**
|
||||
* @param map from annotation qualified name to nullability
|
||||
* @return a data holder implementation based on the provided map
|
||||
*/
|
||||
static @NotNull NullabilityAnnotationDataHolder fromMap(@NotNull Map<String, Nullability> map) {
|
||||
return new NullabilityAnnotationDataHolder() {
|
||||
@Override
|
||||
public Set<String> qualifiedNames() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Nullability getNullability(String annotation) {
|
||||
return map.get(annotation);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.meta.TypeQualifierDefault;
|
||||
import javax.annotation.meta.TypeQualifierNickname;
|
||||
import javax.annotation.meta.When;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Nonnull(when = When.ALWAYS)
|
||||
@TypeQualifierNickname
|
||||
@Target({ElementType.TYPE})
|
||||
@TypeQualifierDefault({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.LOCAL_VARIABLE,
|
||||
ElementType.TYPE_PARAMETER,
|
||||
ElementType.TYPE_USE,
|
||||
})
|
||||
@interface DefaultNonNull {
|
||||
}
|
||||
|
||||
@Nonnull(when = When.MAYBE)
|
||||
@TypeQualifierNickname
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.LOCAL_VARIABLE,
|
||||
ElementType.TYPE_USE,
|
||||
})
|
||||
@interface Nullable {
|
||||
}
|
||||
|
||||
@DefaultNonNull
|
||||
class Test {
|
||||
void test(String s) {
|
||||
if (<warning descr="Condition 's == null' is always 'false'">s == null</warning>) {}
|
||||
@Nullable String @Nullable [] arr = new String[] {"a", "b", "c", <warning descr="'null' is stored to an array of @NotNull elements">null</warning>};
|
||||
@Nullable String @Nullable [] arr2 = {"a", "b", "c", null};
|
||||
@Nullable String @Nullable [] arr3 = new @Nullable String [] {"a", "b", "c", null};
|
||||
}
|
||||
}
|
||||
@@ -179,4 +179,9 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
|
||||
setupTypeUseAnnotations("org.jspecify.annotations", myFixture);
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testJsr305NicknameAsTypeAnnotation() {
|
||||
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user