mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
MethodReturnTypeFix: updated fix to change method return type based on return statements in method body (IDEA-216275)
GitOrigin-RevId: e97990950039123c8c41921a71342b8ed60afdee
This commit is contained in:
committed by
intellij-monorepo-bot
parent
52aa1ae553
commit
42d733a694
@@ -60,6 +60,12 @@ public abstract class QuickFixFactory {
|
||||
@NotNull PsiType toReturn,
|
||||
boolean fixWholeHierarchy);
|
||||
|
||||
@NotNull
|
||||
public abstract LocalQuickFixAndIntentionActionOnPsiElement createMethodReturnFix(@NotNull PsiMethod method,
|
||||
@NotNull PsiType toReturn,
|
||||
boolean fixWholeHierarchy,
|
||||
boolean suggestSuperTypes);
|
||||
|
||||
@NotNull
|
||||
public abstract LocalQuickFixAndIntentionActionOnPsiElement createAddMethodFix(@NotNull PsiMethod method, @NotNull PsiClass toClass);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
|
||||
import com.intellij.lang.jvm.JvmModifier;
|
||||
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
|
||||
import com.intellij.lang.jvm.actions.MemberRequestsKt;
|
||||
import com.intellij.lang.jvm.util.JvmUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
@@ -35,10 +34,13 @@ import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.VisibilityUtil;
|
||||
import com.intellij.util.containers.MostlySingularMultiMap;
|
||||
import com.intellij.util.ui.StartupUiUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.xml.util.XmlStringUtil;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -1196,6 +1198,123 @@ public class HighlightMethodUtil {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkReturnType(@NotNull PsiMethod method) {
|
||||
PsiElement element = method.getNameIdentifier();
|
||||
if (element == null) return null;
|
||||
PsiType returnType = method.getReturnType();
|
||||
PsiType expectedReturnType = constructReturnType(method);
|
||||
if (expectedReturnType == null || expectedReturnType.equals(returnType)) return null;
|
||||
String description = JavaErrorMessages.message("invalid.return.type");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMethodReturnFix(method, expectedReturnType, true, true));
|
||||
return info;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType constructReturnType(@NotNull PsiMethod method) {
|
||||
PsiManager manager = method.getManager();
|
||||
PsiType returnType = method.getReturnType();
|
||||
PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(method);
|
||||
if (returnStatements.length == 0) {
|
||||
return ControlFlowUtils.codeBlockMayCompleteNormally(method.getBody()) ? PsiType.VOID : returnType;
|
||||
}
|
||||
List<ReturnValueType> valueTypes = getValueTypes(returnStatements);
|
||||
if (valueTypes == null) return null;
|
||||
if (isValidReturnType(returnType, valueTypes, method, manager)) return returnType;
|
||||
return StreamEx.of(valueTypes).foldLeft(null, (acc, vt) -> lub(acc, vt.myLeastType, vt.myType, method, manager));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<ReturnValueType> getValueTypes(@NotNull PsiReturnStatement[] statements) {
|
||||
List<ReturnValueType> valueTypes = new ArrayList<>();
|
||||
for (PsiReturnStatement statement : statements) {
|
||||
ReturnValueType valueType = ReturnValueType.create(statement);
|
||||
if (valueType == null) return null;
|
||||
valueTypes.add(valueType);
|
||||
}
|
||||
return valueTypes;
|
||||
}
|
||||
|
||||
@Contract("null, _, _, _ -> false")
|
||||
private static boolean isValidReturnType(@Nullable PsiType returnType, @NotNull List<ReturnValueType> valueTypes,
|
||||
@NotNull PsiMethod method, @NotNull PsiManager manager) {
|
||||
if (returnType == null) return false;
|
||||
return valueTypes.stream()
|
||||
.allMatch(vt -> TypeConversionUtil.isAssignable(returnType, lub(returnType, vt.myLeastType, vt.myType, method, manager)));
|
||||
}
|
||||
|
||||
@Contract("null, _, _, _, _ -> param3")
|
||||
@NotNull
|
||||
private static PsiType lub(@Nullable PsiType returnType,
|
||||
@NotNull PsiType leastValueType,
|
||||
@NotNull PsiType valueType,
|
||||
@NotNull PsiMethod method,
|
||||
@NotNull PsiManager manager) {
|
||||
if (returnType == null || PsiType.VOID.equals(returnType)) return valueType;
|
||||
if (returnType == valueType) return returnType;
|
||||
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(valueType)) {
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(returnType)) {
|
||||
int r1 = TypeConversionUtil.getTypeRank(returnType);
|
||||
int r2 = TypeConversionUtil.getTypeRank(leastValueType);
|
||||
return r1 >= r2 ? returnType : valueType;
|
||||
}
|
||||
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(returnType);
|
||||
if (unboxedType != null && unboxedType.equals(valueType)) return returnType;
|
||||
valueType = ((PsiPrimitiveType)valueType).getBoxedType(method);
|
||||
}
|
||||
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(returnType)) {
|
||||
returnType = ((PsiPrimitiveType)returnType).getBoxedType(method);
|
||||
}
|
||||
|
||||
return Objects.requireNonNull(GenericsUtil.getLeastUpperBound(returnType, valueType, manager));
|
||||
}
|
||||
|
||||
private static class ReturnValueType {
|
||||
final PsiType myType;
|
||||
final PsiType myLeastType;
|
||||
|
||||
@Contract(pure = true)
|
||||
private ReturnValueType(@NotNull PsiType type) {
|
||||
myType = myLeastType = type;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private ReturnValueType(@NotNull PsiType type, @NotNull PsiType leastType) {
|
||||
myType = type;
|
||||
myLeastType = leastType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static ReturnValueType create(@NotNull PsiReturnStatement statement) {
|
||||
PsiExpression value = statement.getReturnValue();
|
||||
if (value == null) return new ReturnValueType(PsiType.VOID);
|
||||
if (ExpressionUtils.nonStructuralChildren(value).anyMatch(c -> c instanceof PsiFunctionalExpression)) return null;
|
||||
PsiType type = value.getType();
|
||||
if (type == null || type instanceof PsiClassType && ((PsiClassType)type).resolve() == null) return null;
|
||||
return new ReturnValueType(type, getLeastValueType(value, type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiType getLeastValueType(@NotNull PsiExpression returnValue, @NotNull PsiType type) {
|
||||
if (type instanceof PsiPrimitiveType) {
|
||||
int rank = TypeConversionUtil.getTypeRank(type);
|
||||
if (rank < TypeConversionUtil.BYTE_RANK || rank > TypeConversionUtil.INT_RANK) return type;
|
||||
PsiConstantEvaluationHelper evaluator = JavaPsiFacade.getInstance(returnValue.getProject()).getConstantEvaluationHelper();
|
||||
Object res = evaluator.computeConstantExpression(returnValue);
|
||||
if (res instanceof Number) {
|
||||
long value = ((Number)res).longValue();
|
||||
if (-128 <= value && value <= 127) return PsiType.BYTE;
|
||||
if (-32768 <= value && value <= 32767) return PsiType.SHORT;
|
||||
if (0 <= value && value <= 0xFFFF) return PsiType.CHAR;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
static HighlightInfo checkConstructorName(@NotNull PsiMethod method) {
|
||||
PsiClass aClass = method.getContainingClass();
|
||||
if (aClass != null) {
|
||||
@@ -1207,6 +1326,10 @@ public class HighlightMethodUtil {
|
||||
if (className != null) {
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createRenameElementFix(method, className));
|
||||
}
|
||||
PsiType returnType = constructReturnType(method);
|
||||
if (returnType != null) {
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMethodReturnFix(method, returnType, false, true));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,6 +672,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (method.isConstructor()) {
|
||||
myHolder.add(HighlightMethodUtil.checkConstructorName(method));
|
||||
}
|
||||
else if (method.getBody() != null) {
|
||||
myHolder.add(HighlightMethodUtil.checkReturnType(method));
|
||||
}
|
||||
myHolder.add(HighlightNamesUtil.highlightMethodName(method, identifier, true, colorsScheme));
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass != null) {
|
||||
|
||||
@@ -145,6 +145,7 @@ make.vararg.parameter.last.text=Move ''{0}'' to the end of the list
|
||||
fix.parameter.type.family=Fix Parameter Type
|
||||
fix.parameter.type.text=Make ''{0}'' take parameter of type ''{1}'' here
|
||||
fix.return.type.family=Fix return type
|
||||
fix.return.type.or.predecessor.text=Make ''{0}'' return ''{1}'' or predecessor
|
||||
fix.return.type.text=Make ''{0}'' return ''{1}''
|
||||
fix.throws.list.family=Fix throws list
|
||||
fix.throws.list.add.exception=Add ''{0}'' to ''{1}'' throws list
|
||||
|
||||
@@ -8,7 +8,14 @@ import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
|
||||
import com.intellij.codeInsight.intention.impl.TypeExpression;
|
||||
import com.intellij.codeInsight.template.Template;
|
||||
import com.intellij.codeInsight.template.TemplateBuilderImpl;
|
||||
import com.intellij.codeInsight.template.TemplateEditingAdapter;
|
||||
import com.intellij.codeInsight.template.TemplateManager;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.command.undo.UndoUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -17,12 +24,15 @@ import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.AnalysisCanceledException;
|
||||
import com.intellij.psi.controlFlow.ControlFlow;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.statistics.StatisticsInfo;
|
||||
import com.intellij.psi.statistics.StatisticsManager;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -32,27 +42,29 @@ import com.intellij.refactoring.changeSignature.OverriderUsageInfo;
|
||||
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
|
||||
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
|
||||
import com.intellij.refactoring.typeMigration.TypeMigrationRules;
|
||||
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiElement implements HighPriorityAction {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.MethodReturnBooleanFix");
|
||||
|
||||
private final SmartTypePointer myReturnTypePointer;
|
||||
private final boolean myFixWholeHierarchy;
|
||||
private final boolean mySuggestSuperTypes;
|
||||
private final String myName;
|
||||
private final String myCanonicalText;
|
||||
|
||||
public MethodReturnTypeFix(@NotNull PsiMethod method, @NotNull PsiType returnType, boolean fixWholeHierarchy) {
|
||||
public MethodReturnTypeFix(@NotNull PsiMethod method, @NotNull PsiType returnType, boolean fixWholeHierarchy, boolean suggestSuperTypes) {
|
||||
super(method);
|
||||
myReturnTypePointer = SmartTypePointerManager.getInstance(method.getProject()).createSmartTypePointer(returnType);
|
||||
myFixWholeHierarchy = fixWholeHierarchy;
|
||||
mySuggestSuperTypes = suggestSuperTypes;
|
||||
myName = method.getName();
|
||||
if (TypeConversionUtil.isNullType(returnType)) {
|
||||
returnType = PsiType.getJavaLangObject(method.getManager(), method.getResolveScope());
|
||||
@@ -70,7 +82,10 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return QuickFixBundle.message("fix.return.type.text", myName, myCanonicalText);
|
||||
if (!mySuggestSuperTypes) return QuickFixBundle.message("fix.return.type.text", myName, myCanonicalText);
|
||||
PsiType type = Objects.requireNonNull(myReturnTypePointer.getType());
|
||||
boolean hasPredecessor = type.getSuperTypes().length != 0;
|
||||
return QuickFixBundle.message(hasPredecessor ? "fix.return.type.or.predecessor.text" : "fix.return.type.text", myName, myCanonicalText);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +106,8 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle
|
||||
myReturnType != null &&
|
||||
myReturnType.isValid()) {
|
||||
final PsiType returnType = myMethod.getReturnType();
|
||||
if (returnType != null && returnType.isValid() && !Comparing.equal(myReturnType, returnType)) {
|
||||
if (returnType == null) return true;
|
||||
if (returnType.isValid() && !Comparing.equal(myReturnType, returnType)) {
|
||||
return PsiTypesUtil.allTypeParametersResolved(myMethod, myReturnType);
|
||||
}
|
||||
}
|
||||
@@ -107,10 +123,89 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle
|
||||
final PsiMethod myMethod = (PsiMethod)startElement;
|
||||
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(myMethod.getContainingFile())) return;
|
||||
PsiType myReturnType = myReturnTypePointer.getType();
|
||||
if (myReturnType == null) return;
|
||||
boolean isNullType = TypeConversionUtil.isNullType(myReturnType);
|
||||
if (isNullType) myReturnType = PsiType.getJavaLangObject(myMethod.getManager(), myMethod.getResolveScope());
|
||||
PsiType returnType = myReturnTypePointer.getType();
|
||||
if (returnType == null) return;
|
||||
boolean isNullType = TypeConversionUtil.isNullType(returnType);
|
||||
PsiType myReturnType = isNullType ? PsiType.getJavaLangObject(myMethod.getManager(), myMethod.getResolveScope()) : returnType;
|
||||
PsiTypeElement typeElement = myMethod.getReturnTypeElement();
|
||||
if (typeElement == null) {
|
||||
WriteCommandAction.runWriteCommandAction(project, QuickFixBundle.message("fix.return.type.family"), null,
|
||||
() -> addReturnType(project, myMethod, myReturnType));
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
|
||||
}
|
||||
PsiType[] superTypes = mySuggestSuperTypes ? myReturnType.getSuperTypes() : PsiType.EMPTY_ARRAY;
|
||||
if ((!isNullType && superTypes.length == 0) || editor == null || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
changeReturnType(project, file, editor, myMethod, myReturnType);
|
||||
return;
|
||||
}
|
||||
List<PsiType> returnTypes = getReturnTypes(superTypes, myReturnType);
|
||||
if (returnTypes.isEmpty()) return;
|
||||
selectReturnType(project, file, editor, returnTypes, myReturnType, myMethod);
|
||||
}
|
||||
|
||||
private static void addReturnType(@NotNull Project project, @NotNull PsiMethod myMethod, @NotNull PsiType myReturnType) {
|
||||
PsiTypeElement typeElement = PsiElementFactory.getInstance(project).createTypeElement(myReturnType);
|
||||
myMethod.addBefore(typeElement, myMethod.getNameIdentifier());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiType> getReturnTypes(@NotNull PsiType[] types, @NotNull PsiType defaultType) {
|
||||
Map<String, PsiType> map = new THashMap<>();
|
||||
String defaultTypeKey = serialize(defaultType);
|
||||
map.put(defaultTypeKey, defaultType);
|
||||
Arrays.stream(types).forEach(t -> map.put(serialize(t), t));
|
||||
|
||||
List<PsiType> ordered = new ArrayList<>();
|
||||
StatisticsManager statisticsManager = StatisticsManager.getInstance();
|
||||
String statsKey = "IntroduceVariable##" + defaultTypeKey;
|
||||
for (StatisticsInfo info : statisticsManager.getAllValues(statsKey)) {
|
||||
String typeKey = info.getValue();
|
||||
PsiType type = map.get(typeKey);
|
||||
if (type != null) {
|
||||
map.remove(typeKey);
|
||||
ordered.add(type);
|
||||
}
|
||||
}
|
||||
ordered.addAll(map.values());
|
||||
return ordered;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String serialize(PsiType type) {
|
||||
if (PsiUtil.resolveClassInType(type) instanceof PsiTypeParameter) return type.getCanonicalText();
|
||||
return TypeConversionUtil.erasure(type).getCanonicalText();
|
||||
}
|
||||
|
||||
void selectReturnType(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
@NotNull List<PsiType> returnTypes,
|
||||
@NotNull PsiType myReturnType,
|
||||
@NotNull PsiMethod myMethod) {
|
||||
PsiTypeElement typeElement = myMethod.getReturnTypeElement();
|
||||
if (typeElement == null) return;
|
||||
TemplateBuilderImpl builder = new TemplateBuilderImpl(typeElement);
|
||||
builder.replaceElement(typeElement, new TypeExpression(project, returnTypes));
|
||||
Template template = WriteCommandAction.runWriteCommandAction(project, (Computable<Template>)() -> builder.buildInlineTemplate());
|
||||
TemplateEditingAdapter listener = new TemplateEditingAdapter() {
|
||||
@Override
|
||||
public void templateFinished(@NotNull Template template, boolean brokenOff) {
|
||||
if (brokenOff) return;
|
||||
PsiType newReturnType = myMethod.getReturnType();
|
||||
if (newReturnType == null) return;
|
||||
TypeSelectorManagerImpl.typeSelected(newReturnType, myReturnType);
|
||||
changeReturnType(project, file, editor, myMethod, newReturnType);
|
||||
}
|
||||
};
|
||||
editor.getCaretModel().moveToOffset(typeElement.getTextOffset());
|
||||
TemplateManager.getInstance(project).startTemplate(editor, template, listener);
|
||||
}
|
||||
|
||||
private void changeReturnType(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
Editor editor,
|
||||
@NotNull PsiMethod myMethod,
|
||||
@NotNull PsiType myReturnType) {
|
||||
if (myFixWholeHierarchy) {
|
||||
final PsiMethod superMethod = myMethod.findDeepestSuperMethod();
|
||||
final PsiType superReturnType = superMethod == null ? null : superMethod.getReturnType();
|
||||
@@ -136,12 +231,6 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle
|
||||
}
|
||||
}
|
||||
|
||||
if (isNullType) {
|
||||
Editor editorForMethod = getEditorForMethod(myMethod, project, editor, file);
|
||||
if (editorForMethod != null) selectInEditor(myMethod.getReturnTypeElement(), editorForMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statementToSelect != null) {
|
||||
Editor editorForMethod = getEditorForMethod(myMethod, project, editor, file);
|
||||
if (editorForMethod != null) {
|
||||
|
||||
@@ -81,7 +81,16 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
public LocalQuickFixAndIntentionActionOnPsiElement createMethodReturnFix(@NotNull PsiMethod method,
|
||||
@NotNull PsiType toReturn,
|
||||
boolean fixWholeHierarchy) {
|
||||
return new MethodReturnTypeFix(method, toReturn, fixWholeHierarchy);
|
||||
return new MethodReturnTypeFix(method, toReturn, fixWholeHierarchy, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LocalQuickFixAndIntentionActionOnPsiElement createMethodReturnFix(@NotNull PsiMethod method,
|
||||
@NotNull PsiType toReturn,
|
||||
boolean fixWholeHierarchy,
|
||||
boolean suggestSuperTypes) {
|
||||
return new MethodReturnTypeFix(method, toReturn, fixWholeHierarchy, suggestSuperTypes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -164,6 +164,7 @@ cannot.resolve.method=Cannot resolve method ''{0}''
|
||||
missing.method.body=Missing method body, or declare abstract
|
||||
abstract.method.in.non.abstract.class=Abstract method in non-abstract class
|
||||
missing.return.type=Invalid method declaration; return type required
|
||||
invalid.return.type=Invalid return type
|
||||
duplicate.method=''{0}'' is already defined in ''{1}''
|
||||
constructor.call.must.be.first.statement=Call to ''{0}'' must be first statement in constructor body
|
||||
direct.abstract.method.access=Abstract method ''{0}'' cannot be accessed directly
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class C {
|
||||
<error descr="Illegal type: 'void'">void</error>[] m1() { }
|
||||
<error descr="Illegal type: 'void'">void</error> m2()[] { }
|
||||
<error descr="Illegal type: 'void'">void</error>[] <error descr="Invalid return type">m1</error>() { }
|
||||
<error descr="Illegal type: 'void'">void</error> <error descr="Invalid return type">m2</error>()[] { }
|
||||
void m3(<error descr="Illegal type: 'void'">void</error> p) {}
|
||||
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ interface ii {}
|
||||
|
||||
|
||||
|
||||
int f1() throws Exception {
|
||||
int <error descr="Invalid return type">f1</error>() throws Exception {
|
||||
<error descr="Missing return statement">}</error>
|
||||
|
||||
Object f2(int i) throws Exception {
|
||||
@@ -217,7 +217,7 @@ interface ii {}
|
||||
|
||||
|
||||
|
||||
int f1() throws Exception {
|
||||
int <error descr="Invalid return type">f1</error>() throws Exception {
|
||||
<error descr="Missing return statement">}</error>
|
||||
|
||||
Object f2(int i) throws Exception {
|
||||
@@ -421,7 +421,7 @@ interface ii {}
|
||||
|
||||
|
||||
|
||||
int f1() throws Exception {
|
||||
int <error descr="Invalid return type">f1</error>() throws Exception {
|
||||
<error descr="Missing return statement">}</error>
|
||||
|
||||
Object f2(int i) throws Exception {
|
||||
@@ -626,7 +626,7 @@ interface ii {}
|
||||
|
||||
|
||||
|
||||
int f1() throws Exception {
|
||||
int <error descr="Invalid return type">f1</error>() throws Exception {
|
||||
<error descr="Missing return statement">}</error>
|
||||
|
||||
Object f2(int i) throws Exception {
|
||||
@@ -829,7 +829,7 @@ interface ii {}
|
||||
|
||||
|
||||
|
||||
int f1() throws Exception {
|
||||
int <error descr="Invalid return type">f1</error>() throws Exception {
|
||||
<error descr="Missing return statement">}</error>
|
||||
|
||||
Object f2(int i) throws Exception {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
class s {
|
||||
void f() {
|
||||
void <error descr="Invalid return type">f</error>() {
|
||||
<error descr="Cannot return a value from a method with void result type">return 0;</error>
|
||||
}
|
||||
void f2() {
|
||||
return;
|
||||
}
|
||||
|
||||
int f3() {
|
||||
int <error descr="Invalid return type">f3</error>() {
|
||||
<error descr="Missing return value">return;</error>
|
||||
}
|
||||
int f4() {
|
||||
|
||||
@@ -6,7 +6,7 @@ class Test {
|
||||
<warning descr="Unchecked call to 'isAssignableFrom(Class<?>)' as a member of raw type 'java.lang.Class'">foo.isAssignableFrom</warning>(Object.class);
|
||||
}
|
||||
|
||||
public List<String> transform(List<List<String>> result) {
|
||||
public List<String> <error descr="Invalid return type">transform</error>(List<List<String>> result) {
|
||||
<error descr="Incompatible types. Found: 'java.util.List<java.util.List<java.lang.String>>', required: 'java.util.List<java.lang.String>'">return result;</error>
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ class Test {
|
||||
public <T> T doStuff() {
|
||||
return null;
|
||||
}
|
||||
public boolean test() {
|
||||
public boolean <error descr="Invalid return type">test</error>() {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'boolean'">return doStuff();</error>
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ public class WrongGenerics {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Set<Foo<? extends T>> bar(Set<Foo<? extends T>> foo) {
|
||||
<T> Set<Foo<? extends T>> <error descr="Invalid return type">bar</error>(Set<Foo<? extends T>> foo) {
|
||||
return <error descr="Inconvertible types; cannot cast 'java.util.Set<Foo<? extends T>>' to 'java.util.Set<Foo<?>>'">(Set<Foo<?>>) foo</error>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Bug {
|
||||
static A test(A[] as) {
|
||||
static A <error descr="Invalid return type">test</error>(A[] as) {
|
||||
for (<error descr="Incompatible types. Found: 'Bug.B', required: 'Bug.A'">B b</error> : as) {
|
||||
<error descr="Incompatible types. Found: 'Bug.B', required: 'Bug.A'">return b;</error>
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class B<S> extends A<S> {
|
||||
|
||||
<error descr="'foo(T, S)' in 'A' clashes with 'foo(Object, Object)' in 'B'; both methods have same erasure, yet neither overrides the other">class C extends B<String></error> {
|
||||
@Override
|
||||
<T> T foo(T x, String y) {
|
||||
<T> T <error descr="Invalid return type">foo</error>(T x, String y) {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">return super.foo(x, y);</error>
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class LimitedPool<T> {
|
||||
void cleanup(T t);
|
||||
}
|
||||
|
||||
public T alloc() {
|
||||
public T <error descr="Invalid return type">alloc</error>() {
|
||||
if (index >= capacity) return factory.create();
|
||||
|
||||
if (storage[index] == null) {
|
||||
|
||||
@@ -76,7 +76,7 @@ class d {
|
||||
}
|
||||
|
||||
class e {
|
||||
String foo () {
|
||||
String <error descr="Invalid return type">foo</error> () {
|
||||
MyList myList = new MyList();
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return myList.get(0);</error>
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class A111<T> {
|
||||
return v;
|
||||
}
|
||||
|
||||
String g(A111 a) {
|
||||
String <error descr="Invalid return type">g</error>(A111 a) {
|
||||
//noinspection unchecked
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return a.f("");</error>
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class A11<T> extends A1 {
|
||||
class Test1<X> {
|
||||
X x;
|
||||
java.util.ArrayList<Number> foo = new java.util.ArrayList<Number>();
|
||||
public static Number foo() {
|
||||
public static Number <error descr="Invalid return type">foo</error>() {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">return new Test1().foo.get(0);</error>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class Test {
|
||||
public static <Om> List<Om> sort(Comparator comp, Stream<Om> stream) {
|
||||
public static <Om> List<Om> <error descr="Invalid return type">sort</error>(Comparator comp, Stream<Om> stream) {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<Om>'">return stream.sorted(comp).collect(Collectors.toList());</error>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Foo<T extends Enum> {
|
||||
public T bar(Class<? extends T> type, String str) {
|
||||
public T <error descr="Invalid return type">bar</error>(Class<? extends T> type, String str) {
|
||||
return <error descr="Incompatible types. Required T but 'valueOf' was inferred to T:
|
||||
Incompatible types: Enum is not convertible to T">Enum.valueOf(type, str);</error>
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class B<S> extends A<S> {
|
||||
|
||||
<error descr="'foo(T, S)' in 'A' clashes with 'foo(Object, Object)' in 'B'; both methods have same erasure, yet neither overrides the other">class C extends B<String></error> {
|
||||
@Override
|
||||
<T> T foo(T x, String y) {
|
||||
<T> T <error descr="Invalid return type">foo</error>(T x, String y) {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">return super.foo(x, y);</error>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class LimitedPool<T> {
|
||||
void cleanup(T t);
|
||||
}
|
||||
|
||||
public T alloc() {
|
||||
public T <error descr="Invalid return type">alloc</error>() {
|
||||
if (index >= capacity) return factory.create();
|
||||
|
||||
if (storage[index] == null) {
|
||||
|
||||
@@ -87,7 +87,7 @@ class d {
|
||||
}
|
||||
|
||||
class e {
|
||||
String foo () {
|
||||
String <error descr="Invalid return type">foo</error> () {
|
||||
MyList myList = new MyList();
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return myList.get(0);</error>
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class A111<T> {
|
||||
return v;
|
||||
}
|
||||
|
||||
String g(A111 a) {
|
||||
String <error descr="Invalid return type">g</error>(A111 a) {
|
||||
//noinspection unchecked
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return a.f("");</error>
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class A11<T> extends A1 {
|
||||
class Test1<X> {
|
||||
X x;
|
||||
java.util.ArrayList<Number> foo = new java.util.ArrayList<Number>();
|
||||
public static Number foo() {
|
||||
public static Number <error descr="Invalid return type">foo</error>() {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">return new Test1().foo.get(0);</error>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ abstract class Group {
|
||||
public Group() {
|
||||
}
|
||||
|
||||
public <T extends Category> T get(Key<T> key) {
|
||||
public <T extends Category> T <error descr="Invalid return type">get</error>(Key<T> key) {
|
||||
return <error descr="Incompatible types. Required T but 'getCategory' was inferred to R:
|
||||
Incompatible types: Category is not convertible to T">getCategory(key);</error>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Outer<T> {
|
||||
class Inner { }
|
||||
Foo<Outer.Inner> m(Foo<Outer<Integer>.Inner> foo) {
|
||||
Foo<Outer.Inner> <error descr="Invalid return type">m</error>(Foo<Outer<Integer>.Inner> foo) {
|
||||
<error descr="Incompatible types. Found: 'Foo<Outer<java.lang.Integer>.Inner>', required: 'Foo<Outer.Inner>'">return foo;</error>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ class Test {
|
||||
return list.stream().flatMap(List::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<Object> test1(List<List> list) {
|
||||
private static List<Object> <error descr="Invalid return type">test1</error>(List<List> list) {
|
||||
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<java.lang.Object>'">return list.stream().flatMap(l -> l.stream()).collect(Collectors.toList());</error>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class ConcurrentCollectors {
|
||||
}
|
||||
|
||||
static class Test3 {
|
||||
static <T, K, D, M1 extends Map<K, D>> C<T, M1> groupingBy(F<M1> f,
|
||||
static <T, K, D, M1 extends Map<K, D>> C<T, M1> <error descr="Invalid return type">groupingBy</error>(F<M1> f,
|
||||
C<T, D> c,
|
||||
BiConsumer<M1, T> consumer) {
|
||||
return new CImpl<error descr="Cannot infer arguments"><></error>(f, consumer, arg(c.getOp()));
|
||||
|
||||
@@ -11,7 +11,7 @@ interface Foo<T> {
|
||||
}
|
||||
|
||||
class Bar {
|
||||
Foo<List<String>> transform(final Foo<? extends String> foo) {
|
||||
Foo<List<String>> <error descr="Invalid return type">transform</error>(final Foo<? extends String> foo) {
|
||||
<error descr="Incompatible types. Found: 'Foo<? extends java.util.List<? extends java.lang.String>>', required: 'Foo<java.util.List<java.lang.String>>'">return foo
|
||||
.map(v2 -> tuple(v2))
|
||||
.onClose();</error>
|
||||
|
||||
@@ -3,7 +3,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class MyTest {
|
||||
static Map<String, Meeting> getMeetingsById(List<Meeting> meetings){
|
||||
static Map<String, Meeting> <error descr="Invalid return type">getMeetingsById</error>(List<Meeting> meetings){
|
||||
return <error descr="Incompatible types. Required Map<String, Meeting> but 'collect' was inferred to R:
|
||||
no instance(s) of type variable(s) A, A, K, R, T exist so that List<T> conforms to Meeting">meetings.stream()
|
||||
.collect(Collectors.groupingBy(Meeting::getId));</error>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Make 'm' return 'java.util.AbstractList<java.lang.Object>' or predecessor" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
|
||||
AbstractList<Object> m(boolean b) {
|
||||
if (b) return new ArrayList<>();
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Make 'm' return 'java.lang.Integer' or predecessor" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
Integer m(boolean b) {
|
||||
if (b) return null;
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'm' return 'int'" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
int m() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Make 'm' return 'void'" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
void m() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'm' return 'double'" "true"
|
||||
class Test {
|
||||
|
||||
double m(boolean b) {
|
||||
if (b) return 42;
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class Test {
|
||||
|
||||
<caret><selection>Object</selection> foo() {
|
||||
Object foo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Make 'm' return 'java.util.AbstractList<java.lang.Object>' or predecessor" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
|
||||
void <caret>m(boolean b) {
|
||||
if (b) return new ArrayList<>();
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Make 'm' return 'java.lang.Integer' or predecessor" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
void <caret>m(boolean b) {
|
||||
if (b) return null;
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'm' return 'int'" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
void <caret>m() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Make 'm' return 'void'" "true"
|
||||
|
||||
class Test {
|
||||
|
||||
int <caret>m() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'm' return 'double'" "true"
|
||||
class Test {
|
||||
|
||||
<caret>m(boolean b) {
|
||||
if (b) return 42;
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,7 @@ interface I2 extends I1 {
|
||||
|
||||
class A implements I1 {
|
||||
@Override
|
||||
public Object foo() {
|
||||
public Object <error descr="Invalid return type">foo</error>() {
|
||||
// returns something
|
||||
<error descr="Missing return statement">}</error>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class A{
|
||||
String toHex(int i) {<error descr="Missing return statement">}</error>
|
||||
String toHex(short i) {<error descr="Missing return statement">}</error>
|
||||
String <error descr="Invalid return type">toHex</error>(int i) {<error descr="Missing return statement">}</error>
|
||||
String <error descr="Invalid return type">toHex</error>(short i) {<error descr="Missing return statement">}</error>
|
||||
|
||||
void f(){
|
||||
String result = toHex((short)'i');
|
||||
|
||||
@@ -64,7 +64,7 @@ public class DataProviderReturnTypeInspection extends AbstractBaseJavaLocalInspe
|
||||
|
||||
String[] applicableReturnTypes = supportOneDimensional ? KNOWN_WITH_ONE_DIMENSIONAL_RETURN_TYPES : KNOWN_RETURN_TYPES;
|
||||
for (String typeText : applicableReturnTypes) {
|
||||
fixes.add(new MethodReturnTypeFix(method, elementFactory.createTypeFromText(typeText, method), false));
|
||||
fixes.add(new MethodReturnTypeFix(method, elementFactory.createTypeFromText(typeText, method), false, false));
|
||||
}
|
||||
|
||||
return fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
|
||||
Reference in New Issue
Block a user