mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
[java-intentions] AddVariableInitializerFix: suggest more suitable initial values instead of null for some types
Fixes IDEA-344453 Intellij should not try to initialize an Optional var with null GitOrigin-RevId: 8097988bf1335a282138e8d09e350c3a5f65204d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
48cb7b5154
commit
5bd02b55c3
@@ -58,7 +58,7 @@ class MethodDuplicatesMatchProvider implements MatchProvider {
|
||||
}
|
||||
}
|
||||
else {
|
||||
methodCallExpression.getArgumentList().add(factory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(parameter.getType()), parameter));
|
||||
methodCallExpression.getArgumentList().add(factory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(parameter.getType(), true), parameter));
|
||||
}
|
||||
}
|
||||
if (needQualifier || needStaticQualifier || nameConflicts) {
|
||||
|
||||
@@ -90,7 +90,7 @@ public class AddReturnFix extends PsiUpdateModCommandAction<PsiParameterListOwne
|
||||
return conversion;
|
||||
}
|
||||
}
|
||||
return PsiTypesUtil.getDefaultValueOfType(type);
|
||||
return PsiTypesUtil.getDefaultValueOfType(type, true);
|
||||
}
|
||||
|
||||
@NonNls
|
||||
|
||||
@@ -5,10 +5,12 @@ import com.intellij.codeInsight.lookup.ExpressionLookupItem;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.template.PsiElementResult;
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode;
|
||||
import com.intellij.codeInspection.util.OptionalUtil;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.modcommand.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
@@ -54,18 +56,19 @@ public class AddVariableInitializerFix extends PsiUpdateModCommandAction<PsiVari
|
||||
|
||||
static LookupElement @NotNull [] suggestInitializer(final PsiVariable variable) {
|
||||
PsiType type = variable.getType();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(variable.getProject());
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(variable.getProject());
|
||||
|
||||
final List<LookupElement> result = new SmartList<>();
|
||||
final String defaultValue = PsiTypesUtil.getDefaultValueOfType(type);
|
||||
final ExpressionLookupItem defaultExpression = new ExpressionLookupItem(elementFactory.createExpressionFromText(defaultValue, variable));
|
||||
result.add(defaultExpression);
|
||||
if (type instanceof PsiClassType) {
|
||||
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
|
||||
result.add(new ExpressionLookupItem(elementFactory.createExpressionFromText("\"\"", variable)));
|
||||
}
|
||||
final PsiClass aClass = PsiTypesUtil.getPsiClass(type);
|
||||
if (aClass != null && !aClass.hasModifierProperty(PsiModifier.ABSTRACT) && PsiUtil.hasDefaultConstructor(aClass)) {
|
||||
List<LookupElement> result = new SmartList<>();
|
||||
String defaultValue = PsiTypesUtil.getDefaultValueOfType(type);
|
||||
String customDefaultValue = PsiTypesUtil.getDefaultValueOfType(type, true);
|
||||
if (!customDefaultValue.equals(defaultValue)) {
|
||||
PsiExpression customDef = elementFactory.createExpressionFromText(customDefaultValue, variable);
|
||||
result.add(new ExpressionLookupItem((PsiExpression)JavaCodeStyleManager.getInstance(variable.getProject()).shortenClassReferences(customDef)));
|
||||
}
|
||||
result.add(new ExpressionLookupItem(elementFactory.createExpressionFromText(defaultValue, variable)));
|
||||
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
if (aClass != null) {
|
||||
if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && PsiUtil.hasDefaultConstructor(aClass)) {
|
||||
String typeText = type.getCanonicalText(false);
|
||||
if (aClass.getTypeParameters().length > 0 && PsiUtil.isLanguageLevel7OrHigher(variable)) {
|
||||
if (!PsiDiamondTypeImpl.haveConstructorsGenericsParameters(aClass)) {
|
||||
|
||||
@@ -75,42 +75,84 @@ public final class PsiTypesUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type to get default value for (null, 0, or false)
|
||||
* @return a string representing an expression for default value of a given type
|
||||
*/
|
||||
@NotNull
|
||||
public static String getDefaultValueOfType(PsiType type) {
|
||||
public static String getDefaultValueOfType(@Nullable PsiType type) {
|
||||
return getDefaultValueOfType(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type to return default value for
|
||||
* @param customDefaultValues if true, non-null values for object types could be returned that represent an absent value
|
||||
* for a specific type (e.g., empty string, empty list, etc.)
|
||||
* @return a string representing an expression for default value of a given type
|
||||
*/
|
||||
@NotNull
|
||||
public static String getDefaultValueOfType(PsiType type, boolean customDefaultValues) {
|
||||
if (type instanceof PsiArrayType) {
|
||||
int count = type.getArrayDimensions() - 1;
|
||||
PsiType componentType = type.getDeepComponentType();
|
||||
|
||||
if (componentType instanceof PsiClassType) {
|
||||
final PsiClassType classType = (PsiClassType)componentType;
|
||||
if (classType.resolve() instanceof PsiTypeParameter) {
|
||||
return PsiKeyword.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PsiType erasedComponentType = TypeConversionUtil.erasure(componentType);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(PsiKeyword.NEW);
|
||||
buffer.append(" ");
|
||||
buffer.append(erasedComponentType.getCanonicalText());
|
||||
buffer.append("[0]");
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.append("[]");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
public static String getDefaultValueOfType(@Nullable PsiType type, boolean customDefaultValues) {
|
||||
if (type instanceof PsiPrimitiveType) {
|
||||
return PsiTypes.booleanType().equals(type) ? PsiKeyword.FALSE : "0";
|
||||
}
|
||||
if (customDefaultValues) {
|
||||
PsiType rawType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : null;
|
||||
if (rawType != null && rawType.equalsToText(CommonClassNames.JAVA_UTIL_OPTIONAL)) {
|
||||
return CommonClassNames.JAVA_UTIL_OPTIONAL + ".empty()";
|
||||
if (type instanceof PsiArrayType) {
|
||||
int count = type.getArrayDimensions() - 1;
|
||||
PsiType componentType = type.getDeepComponentType();
|
||||
|
||||
if (componentType instanceof PsiClassType) {
|
||||
final PsiClassType classType = (PsiClassType)componentType;
|
||||
if (classType.resolve() instanceof PsiTypeParameter) {
|
||||
return PsiKeyword.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PsiType erasedComponentType = TypeConversionUtil.erasure(componentType);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(PsiKeyword.NEW);
|
||||
buffer.append(" ");
|
||||
buffer.append(erasedComponentType.getCanonicalText());
|
||||
buffer.append("[0]");
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.append("[]");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
if (psiClass != null) {
|
||||
String typeText = psiClass.getQualifiedName();
|
||||
if (typeText != null) {
|
||||
switch (typeText) {
|
||||
case CommonClassNames.JAVA_UTIL_OPTIONAL:
|
||||
case "java.util.OptionalInt":
|
||||
case "java.util.OptionalLong":
|
||||
case "java.util.OptionalDouble":
|
||||
case CommonClassNames.JAVA_UTIL_STREAM_STREAM:
|
||||
case CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM:
|
||||
case CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM:
|
||||
case CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM:
|
||||
return typeText + ".empty()";
|
||||
case CommonClassNames.JAVA_LANG_STRING:
|
||||
return "\"\"";
|
||||
case CommonClassNames.JAVA_LANG_INTEGER:
|
||||
case CommonClassNames.JAVA_LANG_LONG:
|
||||
case CommonClassNames.JAVA_LANG_SHORT:
|
||||
case CommonClassNames.JAVA_LANG_BYTE:
|
||||
return "0";
|
||||
case CommonClassNames.JAVA_LANG_FLOAT:
|
||||
return "0f";
|
||||
case CommonClassNames.JAVA_LANG_DOUBLE:
|
||||
return "0.0";
|
||||
case CommonClassNames.JAVA_UTIL_SET:
|
||||
return PsiUtil.isLanguageLevel9OrHigher(psiClass) ? "java.util.Set.of()" : "java.util.Collections.emptySet()";
|
||||
case CommonClassNames.JAVA_UTIL_COLLECTION:
|
||||
case CommonClassNames.JAVA_UTIL_LIST:
|
||||
return PsiUtil.isLanguageLevel9OrHigher(psiClass) ? "java.util.List.of()" : "java.util.Collections.emptyList()";
|
||||
case CommonClassNames.JAVA_UTIL_MAP:
|
||||
return PsiUtil.isLanguageLevel9OrHigher(psiClass) ? "java.util.Map.of()" : "java.util.Collections.emptyMap()";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return PsiKeyword.NULL;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// "Add 'return' statement" "true-preview"
|
||||
class a {
|
||||
String f() {
|
||||
return <caret><selection>null</selection>;
|
||||
return <caret><selection>""</selection>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Initialize variable 'list'" "true-preview"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Collections.emptyList();
|
||||
System.out.println(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Initialize variable 'msg'" "true-preview"
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class OptionalTest {
|
||||
public static void main(String[] args) {
|
||||
Optional<String> msg = Optional.empty();
|
||||
if (args.length > 5) {
|
||||
msg = Optional.of("hello");
|
||||
}
|
||||
System.out.println("msg = "+msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Initialize variable 'string'" "true-preview"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
String string = "";
|
||||
System.out.println(string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Initialize variable 'list'" "true-preview"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
List<String> list;
|
||||
System.out.println(<caret>list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Initialize variable 'msg'" "true-preview"
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class OptionalTest {
|
||||
public static void main(String[] args) {
|
||||
Optional<String> msg;
|
||||
if (args.length > 5) {
|
||||
msg = Optional.of("hello");
|
||||
}
|
||||
System.out.println("msg = "+<caret>msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Initialize variable 'string'" "true-preview"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
String string;
|
||||
System.out.println(<caret>string);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,6 @@ class B extends A<String> {
|
||||
class ABC extends A<Integer> {
|
||||
@Override
|
||||
String foo(Integer integer) {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ class FooImpl extends Foo<String> {
|
||||
|
||||
@Override
|
||||
public @Nullable String getSmth() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ public class ExTest {
|
||||
}
|
||||
|
||||
{
|
||||
String[] a = new String[0];
|
||||
String[] a = null;
|
||||
try {
|
||||
a = new String[]{maybeThrow("")};
|
||||
} catch (Ex e) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
}
|
||||
class d implements ff {
|
||||
public String f() {
|
||||
<caret><selection>return null;</selection>
|
||||
<caret><selection>return "";</selection>
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
|
||||
@@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -46,16 +47,16 @@ class M implements Map {
|
||||
|
||||
@NotNull
|
||||
public Set keySet() {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection values() {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<Entry> entrySet() {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
record R() implements Nameable, Sizable {
|
||||
@Override
|
||||
public String name() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lastName() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,6 @@ record R() {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,6 @@ interface B
|
||||
class C implements A, B
|
||||
{
|
||||
public String foo() {
|
||||
<selection>return null;</selection>
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@ interface I<T> {
|
||||
class C implements I<String> {
|
||||
@Override
|
||||
public String foo(String x) {
|
||||
<selection>return null;</selection>
|
||||
<selection>return "";</selection>
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ record R() {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package p;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -43,17 +44,17 @@ class M implements Map {
|
||||
|
||||
@NN
|
||||
public Set keySet() {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NN
|
||||
public Collection values() {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NN
|
||||
public Set<Entry> entrySet() {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class X {
|
||||
@NotNull String x = null;
|
||||
@NotNull String x = "";
|
||||
}
|
||||
@@ -5,6 +5,6 @@ class X {
|
||||
@NotNull String x;
|
||||
|
||||
X() {
|
||||
x = null;
|
||||
x = "";
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ class A {
|
||||
|
||||
class B extends A {
|
||||
public B(String a) {
|
||||
this(a, null);
|
||||
this(a, "");
|
||||
}
|
||||
public B(String a, final String anObject) {
|
||||
super(a);
|
||||
|
||||
@@ -4,6 +4,6 @@ class Test {
|
||||
}
|
||||
|
||||
void bar() {
|
||||
foo(null);
|
||||
foo("");
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ public class OverrideImplementTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
class C implements I {
|
||||
@Override
|
||||
public @TA List<@TA String> i(@TA String p1, @TA(1) int @TA(3) [] @TA(2) [] p2) throws @TA IllegalArgumentException {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
@@ -338,7 +338,7 @@ public class OverrideImplementTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@TA
|
||||
@Override
|
||||
public List<@TA String> i(@TA String p1, @TA(1) int @TA(3) [] @TA(2) [] p2) throws @TA IllegalArgumentException {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
@@ -455,7 +455,7 @@ public class OverrideImplementTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
class C implements I {
|
||||
@Override
|
||||
public List<String> i(String p) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
@@ -503,7 +503,7 @@ public class OverrideImplementTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@A("")
|
||||
@Override
|
||||
public List<String> i(@A("a") String p) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
|
||||
@@ -555,7 +555,7 @@ public class JavaLiveTemplateTest extends LiveTemplateTestCase {
|
||||
|
||||
class A {
|
||||
{
|
||||
String s = "null";
|
||||
String s = "";
|
||||
s.toString();
|
||||
}
|
||||
}""");
|
||||
|
||||
@@ -12,7 +12,7 @@ class Y implements T<String> {
|
||||
|
||||
@Override
|
||||
public String foo(String s) {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class Y implements T<String> {
|
||||
|
||||
@Override
|
||||
public String getFoo() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class Y implements T<String> {
|
||||
|
||||
@Override
|
||||
public String getFoo() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class W implements T<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer getFoo() {
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user