[java-highlighting] return statement errors migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: 4449ffc2458eeb73fa296453f6963a6eeed28a76
This commit is contained in:
Tagir Valeev
2025-01-27 18:52:56 +00:00
committed by intellij-monorepo-bot
parent 0831efe6c2
commit 79c582597a
41 changed files with 232 additions and 221 deletions
@@ -171,6 +171,7 @@ method.inheritance.clash.unrelated.return.types={0}; methods have unrelated retu
method.inheritance.clash.incompatible.return.types={0}; attempting to use incompatible return type
method.inheritance.clash.does.not.throw={0}; overridden method does not throw ''{1}''
method.no.parameter.list=Parameter list expected
method.missing.return.type=Invalid method declaration; return type required
clash.methods.message=''{0}'' clashes with ''{1}''
clash.methods.message.show.classes=''{0}'' in ''{2}'' clashes with ''{1}'' in ''{3}''
@@ -322,3 +323,11 @@ unnamed.method.parameter.not.allowed=Unnamed method parameter is not allowed
unnamed.variable.not.allowed.in.this.context=Unnamed variable declaration is not allowed in this context
unnamed.variable.brackets=Brackets are not allowed after an unnamed variable declaration
unnamed.variable.without.initializer=Unnamed variable declaration must have an initializer
return.outside.switch.expression=Return outside of enclosing switch expression
return.compact.constructor='return' statement is not allowed in compact constructor
return.outside.method=Return outside method
return.before.explicit.constructor.call='return' not allowed before ''{0}'' call
return.value.missing=Missing return value
return.from.void.method=Cannot return a value from a method with void result type
return.from.constructor=Cannot return a value from a constructor
@@ -382,6 +382,26 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) visitExpression(expression);
}
@Override
public void visitReturnStatement(@NotNull PsiReturnStatement statement) {
super.visitReturnStatement(statement);
if (!hasErrorResults()) myStatementChecker.checkReturnStatement(statement);
}
@Override
public void visitReferenceParameterList(@NotNull PsiReferenceParameterList list) {
super.visitReferenceParameterList(list);
if (list.getTextLength() == 0) return;
checkFeature(list, JavaFeature.GENERICS);
if (!hasErrorResults()) {
for (PsiTypeElement typeElement : list.getTypeParameterElements()) {
if (typeElement.getType() instanceof PsiDiamondType) {
checkFeature(list, JavaFeature.DIAMOND_TYPES);
}
}
}
}
@Override
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
PsiElement parent = identifier.getParent();
@@ -405,6 +425,9 @@ final class JavaErrorVisitor extends JavaElementVisitor {
}
else if (parent instanceof PsiMethod method) {
myClassChecker.checkImplicitClassMember(method);
if (method.isConstructor()) {
myMethodChecker.checkConstructorName(method);
}
}
myExpressionChecker.checkUnderscore(identifier);
}
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
@@ -400,6 +401,16 @@ final class MethodChecker {
}
}
void checkConstructorName(PsiMethod method) {
PsiClass aClass = method.getContainingClass();
if (aClass != null) {
String className = aClass instanceof PsiAnonymousClass ? null : aClass.getName();
if (className == null || !Comparing.strEqual(method.getName(), className)) {
myVisitor.report(JavaErrorKinds.METHOD_MISSING_RETURN_TYPE.create(method, className));
}
}
}
static @Nullable TextRange getCStyleDeclarationRange(@NotNull PsiVariable variable) {
PsiIdentifier identifier = variable.getNameIdentifier();
TextRange range = null;
@@ -6,11 +6,12 @@ import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,6 +21,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import static com.intellij.util.ObjectUtils.tryCast;
final class StatementChecker {
private final @NotNull JavaErrorVisitor myVisitor;
@@ -198,4 +201,61 @@ final class StatementChecker {
PsiClass upperCatchClass = PsiUtil.resolveClassInType(upperCatchType);
return upperCatchClass != null && InheritanceUtil.isInheritorOrSelf(catchClass, upperCatchClass, true);
}
void checkReturnStatement(@NotNull PsiReturnStatement statement) {
if (myVisitor.isApplicable(JavaFeature.ENHANCED_SWITCH) && PsiImplUtil.findEnclosingSwitchExpression(statement) != null) {
myVisitor.report(JavaErrorKinds.RETURN_OUTSIDE_SWITCH_EXPRESSION.create(statement));
return;
}
PsiElement parent = PsiTreeUtil.getParentOfType(statement, PsiFile.class, PsiClassInitializer.class,
PsiLambdaExpression.class, PsiMethod.class);
if (parent instanceof PsiMethod method) {
if (JavaPsiRecordUtil.isCompactConstructor(method)) {
myVisitor.report(JavaErrorKinds.RETURN_COMPACT_CONSTRUCTOR.create(statement));
return;
}
if (method.isConstructor()) {
PsiMethodCallExpression constructorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method);
if (constructorCall != null && statement.getTextOffset() < constructorCall.getTextOffset()) {
myVisitor.report(JavaErrorKinds.RETURN_BEFORE_EXPLICIT_CONSTRUCTOR_CALL.create(statement, constructorCall));
return;
}
}
}
if (parent != null) {
checkReturnStatementType(statement, parent);
}
}
void checkReturnStatementType(@NotNull PsiReturnStatement statement, @NotNull PsiElement parent) {
if (parent instanceof PsiCodeFragment || parent instanceof PsiLambdaExpression) return;
PsiMethod method = tryCast(parent, PsiMethod.class);
if (method == null && !(parent instanceof ServerPageFile)) {
myVisitor.report(JavaErrorKinds.RETURN_OUTSIDE_METHOD.create(statement));
return;
}
PsiType returnType = method != null ? method.getReturnType() : null/*JSP page returns void*/;
boolean isMethodVoid = returnType == null || PsiTypes.voidType().equals(returnType);
PsiExpression returnValue = statement.getReturnValue();
if (returnValue != null) {
PsiType valueType = RefactoringChangeUtil.getTypeByExpression(returnValue);
if (isMethodVoid) {
boolean constructor = method != null && method.isConstructor();
if (constructor) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null && !method.getName().equals(containingClass.getName())) {
return;
}
}
var kind = constructor ? JavaErrorKinds.RETURN_FROM_CONSTRUCTOR : JavaErrorKinds.RETURN_FROM_VOID_METHOD;
myVisitor.report(kind.create(statement, method));
}
else {
myVisitor.myExpressionChecker.checkAssignability(returnType, valueType, returnValue, returnValue);
}
}
else if (!isMethodVoid && !PsiTreeUtil.hasErrorElements(statement)) {
myVisitor.report(JavaErrorKinds.RETURN_VALUE_MISSING.create(statement, method));
}
}
}
@@ -559,6 +559,9 @@ public final class JavaErrorKinds {
.withRawDescription((cls, ctx) -> message("method.inheritance.clash.does.not.throw",
formatClashMethodMessage(ctx.method(), ctx.superMethod(), true),
formatType(ctx.exceptionType())));
public static final Parameterized<PsiMethod, String> METHOD_MISSING_RETURN_TYPE =
parameterized(PsiMethod.class, String.class, "method.missing.return.type")
.withAnchor((method, className) -> requireNonNullElse(method.getNameIdentifier(), method));
public static final Parameterized<PsiMember, AmbiguousImplicitConstructorCallContext> CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL =
parameterized(PsiMember.class, AmbiguousImplicitConstructorCallContext.class, "constructor.ambiguous.implicit.call")
@@ -914,6 +917,22 @@ public final class JavaErrorKinds {
public static final Simple<PsiVariable> UNNAMED_VARIABLE_NOT_ALLOWED_IN_THIS_CONTEXT =
error(PsiVariable.class, "unnamed.variable.not.allowed.in.this.context")
.withRange(var -> TextRange.create(0, requireNonNull(var.getNameIdentifier()).getTextRangeInParent().getEndOffset()));
public static final Simple<PsiReturnStatement> RETURN_OUTSIDE_SWITCH_EXPRESSION =
error(PsiReturnStatement.class, "return.outside.switch.expression");
public static final Simple<PsiReturnStatement> RETURN_COMPACT_CONSTRUCTOR =
error(PsiReturnStatement.class, "return.compact.constructor");
public static final Simple<PsiReturnStatement> RETURN_OUTSIDE_METHOD =
error(PsiReturnStatement.class, "return.outside.method");
public static final Parameterized<PsiReturnStatement, PsiMethod> RETURN_VALUE_MISSING =
parameterized(PsiReturnStatement.class, PsiMethod.class, "return.value.missing");
public static final Parameterized<PsiReturnStatement, PsiMethod> RETURN_FROM_CONSTRUCTOR =
parameterized(PsiReturnStatement.class, PsiMethod.class, "return.from.constructor");
public static final Parameterized<PsiReturnStatement, PsiMethod> RETURN_FROM_VOID_METHOD =
parameterized(PsiReturnStatement.class, PsiMethod.class, "return.from.void.method");
public static final Parameterized<PsiReturnStatement, PsiMethodCallExpression> RETURN_BEFORE_EXPLICIT_CONSTRUCTOR_CALL =
parameterized(PsiReturnStatement.class, PsiMethodCallExpression.class, "return.before.explicit.constructor.call")
.withRawDescription((psi, call) -> message("return.before.explicit.constructor.call", call.getMethodExpression().getText() + "()"));
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(
@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) {
@@ -310,8 +310,9 @@ public final class HighlightFixUtil {
}
static void registerChangeParameterClassFix(@NotNull PsiType lType,
@NotNull PsiType rType,
@Nullable PsiType rType,
@NotNull Consumer<? super CommonIntentionAction> info) {
if (rType == null) return;
IntentionAction action = getChangeParameterClassFix(lType, rType);
if (action != null) {
info.accept(action);
@@ -557,25 +557,6 @@ public final class HighlightMethodUtil {
return errorResult;
}
static HighlightInfo.Builder checkConstructorName(@NotNull PsiMethod method) {
PsiClass aClass = method.getContainingClass();
if (aClass != null) {
String className = aClass instanceof PsiAnonymousClass ? null : aClass.getName();
if (className == null || !Comparing.strEqual(method.getName(), className)) {
PsiElement element = ObjectUtils.notNull(method.getNameIdentifier(), method);
String description = JavaErrorBundle.message("missing.return.type");
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description);
if (className != null) {
IntentionAction action = QuickFixFactory.getInstance().createRenameElementFix(method, className);
info.registerFix(action, null, null, null, null);
}
return info;
}
}
return null;
}
static HighlightInfo.Builder checkConstructorHandleSuperClassExceptions(@NotNull PsiMethod method) {
if (!method.isConstructor()) {
return null;
@@ -621,17 +602,19 @@ public final class HighlightMethodUtil {
return builder.toString();
}
static PsiType determineReturnType(@NotNull PsiMethod method) {
PsiManager manager = method.getManager();
PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(method);
if (returnStatements.length == 0) return PsiTypes.voidType();
PsiType expectedType = null;
for (PsiReturnStatement returnStatement : returnStatements) {
ReturnModel returnModel = ReturnModel.create(returnStatement);
if (returnModel == null) return null;
expectedType = lub(expectedType, returnModel.myLeastType, returnModel.myType, method, manager);
}
return expectedType;
static @Nullable PsiType determineReturnType(@NotNull PsiMethod method) {
return CachedValuesManager.getCachedValue(method, () -> {
PsiManager manager = method.getManager();
PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(method);
if (returnStatements.length == 0) return CachedValueProvider.Result.create(PsiTypes.voidType(), method);
PsiType expectedType = null;
for (PsiReturnStatement returnStatement : returnStatements) {
ReturnModel returnModel = ReturnModel.create(returnStatement);
if (returnModel == null) return CachedValueProvider.Result.create(null, method);
expectedType = lub(expectedType, returnModel.myLeastType, returnModel.myType, method, manager);
}
return CachedValueProvider.Result.create(expectedType, method);
});
}
private static @NotNull PsiType lub(@Nullable PsiType currentType,
@@ -47,7 +47,6 @@ import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.NewUI;
import com.intellij.util.ArrayUtilRt;
@@ -411,81 +410,6 @@ public final class HighlightUtil {
return fix -> highlightInfo.registerFix(fix.asIntention(), null, null, null, null);
}
static HighlightInfo.Builder checkReturnFromSwitchExpr(@NotNull PsiReturnStatement statement) {
if (PsiImplUtil.findEnclosingSwitchExpression(statement) != null) {
String message = JavaErrorBundle.message("return.outside.switch.expr");
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message);
if (statement.getReturnValue() != null) {
var action = new ReplaceWithYieldFix(statement);
info.registerFix(action, null, null, null, null);
}
return info;
}
return null;
}
static HighlightInfo.Builder checkReturnStatementType(@NotNull PsiReturnStatement statement, @NotNull PsiElement parent) {
if (parent instanceof PsiCodeFragment || parent instanceof PsiLambdaExpression) {
return null;
}
PsiMethod method = tryCast(parent, PsiMethod.class);
String description;
HighlightInfo.Builder errorResult;
if (method == null && !(parent instanceof ServerPageFile)) {
description = JavaErrorBundle.message("return.outside.method");
errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description);
}
else {
PsiType returnType = method != null ? method.getReturnType() : null/*JSP page returns void*/;
boolean isMethodVoid = returnType == null || PsiTypes.voidType().equals(returnType);
PsiExpression returnValue = statement.getReturnValue();
if (returnValue != null) {
PsiType valueType = RefactoringChangeUtil.getTypeByExpression(returnValue);
if (isMethodVoid) {
boolean constructor = method != null && method.isConstructor();
if (constructor) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null && !method.getName().equals(containingClass.getName())) {
return null;
}
}
description = JavaErrorBundle.message(constructor ? "return.from.constructor" : "return.from.void.method");
errorResult =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description);
if (method != null && valueType != null && method.getBody() != null) {
IntentionAction action1 = getFixFactory().createDeleteReturnFix(method, statement);
errorResult.registerFix(action1, null, null, null, null);
IntentionAction action = getFixFactory().createMethodReturnFix(method, valueType, true);
errorResult.registerFix(action, null, null, null, null);
}
}
else {
TextRange textRange = statement.getTextRange();
errorResult = checkAssignability(returnType, valueType, returnValue, textRange, returnValue.getStartOffsetInParent());
if (errorResult != null && valueType != null) {
if (!PsiTypes.voidType().equals(valueType)) {
IntentionAction action = getFixFactory().createMethodReturnFix(method, valueType, true);
errorResult.registerFix(action, null, null, null, null);
}
HighlightFixUtil.registerChangeParameterClassFix(returnType, valueType, asConsumer(errorResult));
}
}
}
else if (!isMethodVoid && !PsiTreeUtil.hasErrorElements(statement)) {
description = JavaErrorBundle.message("missing.return.value");
errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description)
.navigationShift(PsiKeyword.RETURN.length());
IntentionAction action = getFixFactory().createMethodReturnFix(method, PsiTypes.voidType(), true);
errorResult.registerFix(action, null, null, null, null);
}
else {
errorResult = null;
}
}
return errorResult;
}
static void registerReturnTypeFixes(@NotNull HighlightInfo.Builder info, @NotNull PsiMethod method, @NotNull PsiType expectedReturnType) {
IntentionAction action = getFixFactory().createMethodReturnFix(method, expectedReturnType, true, true);
info.registerFix(action, null, null, null, null);
@@ -823,8 +747,8 @@ public final class HighlightUtil {
boolean allowedError = false;
if (statement instanceof PsiExpressionStatement) {
PsiElement[] children = statement.getChildren();
if (children[0] instanceof PsiExpression && children[1] instanceof PsiErrorElement &&
((PsiErrorElement)children[1]).getErrorDescription().equals(JavaPsiBundle.message("expected.semicolon"))) {
if (children[0] instanceof PsiExpression && children[1] instanceof PsiErrorElement errorElement &&
errorElement.getErrorDescription().equals(JavaPsiBundle.message("expected.semicolon"))) {
allowedError = true;
anchor = children[0];
}
@@ -33,7 +33,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.pom.java.JavaFeature;
@@ -49,7 +48,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.NewUI;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
@@ -92,14 +90,11 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
// map codeBlock->List of PsiReferenceExpression of extra initialization of final variable
private final Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> myFinalVarProblems = new HashMap<>();
private final Map<String, Pair<PsiImportStaticReferenceElement, PsiClass>> mySingleImportedClasses = new HashMap<>();
private final Map<String, Pair<PsiImportStaticReferenceElement, PsiField>> mySingleImportedFields = new HashMap<>();
private final @NotNull Consumer<? super HighlightInfo.Builder> myErrorSink = builder -> add(builder);
private final Set<PsiClass> myOverrideEquivalentMethodsVisitedClasses = new HashSet<>();
// stored "clashing signatures" errors for the method (if the key is a PsiModifierList of the method), or the class (if the key is a PsiModifierList of the class)
private final Map<PsiMember, HighlightInfo.Builder> myOverrideEquivalentMethodsErrors = new HashMap<>();
private final Map<PsiMethod, PsiType> myExpectedReturnTypes = new HashMap<>();
private final Function<? super PsiElement, ? extends PsiMethod> mySurroundingConstructor = entry -> findSurroundingConstructor(entry);
private final Map<PsiElement, PsiMethod> myInsideConstructorOfClassCache = new HashMap<>(); // null value means "cached but no corresponding ctr found"
private boolean myHasError; // true if myHolder.add() was called with HighlightInfo of >=ERROR severity. On each .visit(PsiElement) call this flag is reset. Useful to determine whether the error was already reported while visiting this PsiElement.
@@ -193,8 +188,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
finally {
myUninitializedVarProblems.clear();
myFinalVarProblems.clear();
mySingleImportedClasses.clear();
mySingleImportedFields.clear();
myJavaModule = null;
myFile = null;
myHolder = null;
@@ -202,7 +195,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myPreviewFeatureVisitor = null;
myOverrideEquivalentMethodsVisitedClasses.clear();
myOverrideEquivalentMethodsErrors.clear();
myExpectedReturnTypes.clear();
myInsideConstructorOfClassCache.clear();
}
@@ -576,16 +568,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
else if (parent instanceof PsiMethod method) {
if (method.isConstructor()) {
HighlightInfo.Builder info = HighlightMethodUtil.checkConstructorName(method);
if (info != null) {
PsiType expectedType = myExpectedReturnTypes.computeIfAbsent(method, HighlightMethodUtil::determineReturnType);
if (expectedType != null) {
HighlightUtil.registerReturnTypeFixes(info, method, expectedType);
}
}
add(info);
}
PsiClass aClass = method.getContainingClass();
if (aClass != null) {
add(GenericsHighlightUtil.checkDefaultMethodOverridesMemberOfJavaLangObject(myLanguageLevel, aClass, method, identifier));
@@ -597,6 +579,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitImportStatement(@NotNull PsiImportStatement statement) {
super.visitImportStatement(statement);
if (!hasErrorResults()) {
PreviewFeatureUtil.checkPreviewFeature(statement, myPreviewFeatureVisitor);
}
@@ -687,7 +670,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitNewExpression(@NotNull PsiNewExpression expression) {
PsiType type = expression.getType();
if (!hasErrorResults()) add(GenericsHighlightUtil.checkTypeParameterInstantiation(expression));
if (!hasErrorResults()) visitExpression(expression);
@@ -1144,58 +1126,11 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitReferenceParameterList(@NotNull PsiReferenceParameterList list) {
super.visitReferenceParameterList(list);
if (list.getTextLength() == 0) return;
add(checkFeature(list, JavaFeature.GENERICS));
if (!hasErrorResults()) add(GenericsHighlightUtil.checkParametersAllowed(list));
if (!hasErrorResults()) add(GenericsHighlightUtil.checkParametersOnRaw(list, myLanguageLevel));
if (!hasErrorResults()) {
for (PsiTypeElement typeElement : list.getTypeParameterElements()) {
if (typeElement.getType() instanceof PsiDiamondType) {
add(checkFeature(list, JavaFeature.DIAMOND_TYPES));
}
}
}
}
@Override
public void visitReturnStatement(@NotNull PsiReturnStatement statement) {
super.visitStatement(statement);
if (!hasErrorResults() && PsiUtil.isAvailable(JavaFeature.ENHANCED_SWITCH, myFile)) {
add(HighlightUtil.checkReturnFromSwitchExpr(statement));
}
if (!hasErrorResults()) {
try {
PsiElement parent = PsiTreeUtil.getParentOfType(statement, PsiFile.class, PsiClassInitializer.class,
PsiLambdaExpression.class, PsiMethod.class);
if (parent instanceof PsiMethod method ) {
if (JavaPsiRecordUtil.isCompactConstructor(method)) {
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement)
.descriptionAndTooltip(JavaErrorBundle.message("record.compact.constructor.return")));
}
else if (method.isConstructor()) {
PsiMethodCallExpression constructorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method);
if (constructorCall != null && statement.getTextOffset() < constructorCall.getTextOffset()) {
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement)
.descriptionAndTooltip(JavaErrorBundle.message("return.statement.not.allowed.before.explicit.constructor.call",
constructorCall.getMethodExpression().getText() + "()")));
}
}
}
if (!hasErrorResults() && parent != null) {
HighlightInfo.Builder info = HighlightUtil.checkReturnStatementType(statement, parent);
if (info != null && parent instanceof PsiMethod method) {
PsiType expectedType = myExpectedReturnTypes.computeIfAbsent(method, HighlightMethodUtil::determineReturnType);
if (expectedType != null && !PsiTypes.voidType().equals(expectedType)) {
HighlightUtil.registerReturnTypeFixes(info, method, expectedType);
}
}
add(info);
}
}
catch (IndexNotReadyException ignore) {
}
}
}
@Override
@@ -31,6 +31,7 @@ import com.intellij.psi.impl.light.LightRecordMethod;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.TypeUtils;
@@ -110,6 +111,7 @@ final class JavaErrorFixProvider {
createMethodFixes();
createExpressionFixes();
createVariableFixes();
createStatementFixes();
createExceptionFixes();
createGenericFixes();
createRecordFixes();
@@ -119,6 +121,34 @@ final class JavaErrorFixProvider {
createReceiverParameterFixes();
}
private void createStatementFixes() {
fix(RETURN_OUTSIDE_SWITCH_EXPRESSION, error -> error.psi().getReturnValue() != null ? new ReplaceWithYieldFix(error.psi()) : null);
fixes(RETURN_VALUE_MISSING, (error, sink) -> {
PsiMethod method = error.context();
sink.accept(myFactory.createMethodReturnFix(method, PsiTypes.voidType(), true));
PsiType expectedType = HighlightMethodUtil.determineReturnType(method);
if (expectedType != null && !PsiTypes.voidType().equals(expectedType)) {
sink.accept(myFactory.createMethodReturnFix(method, expectedType, true, true));
}
});
JavaFixesPusher<PsiReturnStatement, PsiMethod> fixReturnFromVoid = (error, sink) -> {
PsiMethod method = error.context();
if (method != null && method.getBody() != null) {
PsiType valueType = RefactoringChangeUtil.getTypeByExpression(requireNonNull(error.psi().getReturnValue()));
if (valueType != null) {
sink.accept(myFactory.createDeleteReturnFix(method, error.psi()));
sink.accept(myFactory.createMethodReturnFix(method, valueType, true));
}
PsiType expectedType = HighlightMethodUtil.determineReturnType(method);
if (expectedType != null && !PsiTypes.voidType().equals(expectedType) && !expectedType.equals(valueType)) {
sink.accept(myFactory.createMethodReturnFix(method, expectedType, true, true));
}
}
};
fixes(RETURN_FROM_CONSTRUCTOR, fixReturnFromVoid);
fixes(RETURN_FROM_VOID_METHOD, fixReturnFromVoid);
}
private void createMethodFixes() {
JavaFixProvider<PsiMethod, Void> addBody = error -> myFactory.createAddMethodBodyFix(error.psi());
fix(METHOD_DEFAULT_SHOULD_HAVE_BODY, addBody);
@@ -176,6 +206,17 @@ final class JavaErrorFixProvider {
}
});
fix(VARARG_CSTYLE_DECLARATION, error -> new NormalizeBracketsFix(error.psi()));
fixes(METHOD_MISSING_RETURN_TYPE, (error, sink) -> {
String className = error.context();
PsiMethod method = error.psi();
if (className != null) {
sink.accept(myFactory.createRenameElementFix(method, className));
}
PsiType expectedType = HighlightMethodUtil.determineReturnType(method);
if (expectedType != null) {
sink.accept(myFactory.createMethodReturnFix(method, expectedType, true, true));
}
});
}
private void createExceptionFixes() {
@@ -424,9 +465,13 @@ final class JavaErrorFixProvider {
PsiType sameType = JavaHighlightUtil.sameType(initializerList.getInitializers());
sink.accept(sameType == null ? null : VariableArrayTypeFix.createFix(initializerList, sameType));
}
else if (parent instanceof PsiReturnStatement && rType != null) {
if (PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class) instanceof PsiMethod containingMethod) {
sink.accept(myFactory.createMethodReturnFix(containingMethod, rType, true, true));
else if (parent instanceof PsiReturnStatement && rType != null && !PsiTypes.voidType().equals(rType)) {
if (PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class) instanceof PsiMethod method) {
sink.accept(myFactory.createMethodReturnFix(method, rType, true, true));
PsiType expectedType = HighlightMethodUtil.determineReturnType(method);
if (expectedType != null && !PsiTypes.voidType().equals(expectedType) && !expectedType.equals(rType)) {
sink.accept(myFactory.createMethodReturnFix(method, expectedType, true, true));
}
}
}
else if (parent instanceof PsiLocalVariable var && rType != null) {
@@ -445,6 +490,7 @@ final class JavaErrorFixProvider {
if (anchor instanceof PsiParameter parameter && parent instanceof PsiForeachStatement forEach) {
HighlightFixUtil.registerChangeVariableTypeFixes(parameter, rType, forEach.getIteratedValue(), sink);
}
HighlightFixUtil.registerChangeParameterClassFix(lType, rType, sink);
});
fixes(CALL_TYPE_INFERENCE_ERROR, (error, sink) -> {
if (error.psi() instanceof PsiMethodCallExpression callExpression) {
@@ -17,12 +17,12 @@ class Foo<T> {
public static Class<? extends Foo<?>> fFoo(final Foo<?> foo) {
<error descr="Incompatible types. Found: 'java.lang.Class<capture<? extends Foo>>', required: 'java.lang.Class<? extends Foo<?>>'">Class<? extends Foo<?>> fooClass = foo.getClass();</error>
<error descr="Incompatible types. Found: 'java.lang.Class<capture<? extends Foo>>', required: 'java.lang.Class<? extends Foo<?>>'">fooClass = foo.getClass()</error>;
<error descr="Incompatible types. Found: 'java.lang.Class<? extends Foo>', required: 'java.lang.Class<? extends Foo<?>>'">return foo.getClass();</error>
return foo.<error descr="Incompatible types. Found: 'java.lang.Class<? extends Foo>', required: 'java.lang.Class<? extends Foo<?>>'">getClass</error>();
}
public static Class<? extends Foo<? extends String>> fFoo1(final Foo<? extends String> foo) {
<error descr="Incompatible types. Found: 'java.lang.Class<capture<? extends Foo>>', required: 'java.lang.Class<? extends Foo<? extends java.lang.String>>'">Class<? extends Foo<? extends String>> fooClass = foo.getClass();</error>
<error descr="Incompatible types. Found: 'java.lang.Class<capture<? extends Foo>>', required: 'java.lang.Class<? extends Foo<? extends java.lang.String>>'">fooClass = foo.getClass()</error>;
<error descr="Incompatible types. Found: 'java.lang.Class<? extends Foo>', required: 'java.lang.Class<? extends Foo<? extends java.lang.String>>'">return foo.getClass();</error>
return foo.<error descr="Incompatible types. Found: 'java.lang.Class<? extends Foo>', required: 'java.lang.Class<? extends Foo<? extends java.lang.String>>'">getClass</error>();
}
}
@@ -7,6 +7,6 @@ class Test {
}
public List<String> transform(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>
return <error descr="Incompatible types. Found: 'java.util.List<java.util.List<java.lang.String>>', required: 'java.util.List<java.lang.String>'">result</error>;
}
}
@@ -3,7 +3,7 @@ class Test {
return null;
}
public boolean test() {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'boolean'">return doStuff();</error>
return <error descr="Incompatible types. Found: 'java.lang.Object', required: 'boolean'">doStuff</error>();
}
public Boolean test1() {
@@ -1,7 +1,7 @@
class Bug {
static A test(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>
return <error descr="Incompatible types. Found: 'Bug.B', required: 'Bug.A'">b</error>;
}
return null;
}
@@ -13,6 +13,6 @@ 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) {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">return super.foo(x, y);</error>
return super.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">foo</error>(x, y);
}
}
@@ -23,7 +23,7 @@ class LimitedPool<T> {
storage[index] = factory.create();
}
<error descr="Incompatible types. Found: 'java.lang.Object[]', required: 'T'">return storage;</error>
return <error descr="Incompatible types. Found: 'java.lang.Object[]', required: 'T'">storage</error>;
}
}
@@ -78,7 +78,7 @@ class d {
class e {
String foo () {
MyList myList = new MyList();
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return myList.get(0);</error>
return myList.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">get</error>(0);
}
static class MyList<<warning descr="Type parameter 'T' is never used">T</warning>> extends ArrayList<String>{
@@ -130,7 +130,7 @@ class A111<T> {
String g(A111 a) {
//noinspection unchecked
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return a.f("");</error>
return a.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">f</error>("");
}
}
@@ -152,7 +152,7 @@ class Test1<X> {
X x;
java.util.ArrayList<Number> foo = new java.util.ArrayList<Number>();
public static Number foo() {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">return new Test1().foo.get(0);</error>
return new Test1().foo.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">get</error>(0);
}
}
//end of IDEADEV-26163
@@ -5,7 +5,7 @@ import java.util.stream.Stream;
class Test {
public static <Om> List<Om> sort(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>
return stream.sorted(comp).<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<Om>'">collect</error>(Collectors.toList());
}
//accept unbounded wildcards
@@ -13,6 +13,6 @@ 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) {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">return super.foo(x, y);</error>
return super.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'T'">foo</error>(x, y);
}
}
@@ -23,7 +23,7 @@ class LimitedPool<T> {
storage[index] = factory.create();
}
<error descr="Incompatible types. Found: 'java.lang.Object[]', required: 'T'">return storage;</error>
return <error descr="Incompatible types. Found: 'java.lang.Object[]', required: 'T'">storage</error>;
}
}
@@ -89,7 +89,7 @@ class d {
class e {
String foo () {
MyList myList = new MyList();
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return myList.get(0);</error>
return myList.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">get</error>(0);
}
static class MyList<<warning descr="Type parameter 'T' is never used">T</warning>> extends ArrayList<String>{
@@ -141,7 +141,7 @@ class A111<T> {
String g(A111 a) {
//noinspection unchecked
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">return a.f("");</error>
return a.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">f</error>("");
}
}
@@ -163,7 +163,7 @@ class Test1<X> {
X x;
java.util.ArrayList<Number> foo = new java.util.ArrayList<Number>();
public static Number foo() {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">return new Test1().foo.get(0);</error>
return new Test1().foo.<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Number'">get</error>(0);
}
}
//end of IDEADEV-26163
@@ -1,7 +1,7 @@
class Outer<T> {
class Inner { }
Foo<Outer.Inner> m(Foo<Outer<Integer>.Inner> foo) {
<error descr="Incompatible types. Found: 'Foo<Outer<java.lang.Integer>.Inner>', required: 'Foo<Outer.Inner>'">return foo;</error>
return <error descr="Incompatible types. Found: 'Foo<Outer<java.lang.Integer>.Inner>', required: 'Foo<Outer.Inner>'">foo</error>;
}
}
class Foo<X> {}
@@ -3,10 +3,10 @@ import java.util.stream.Collectors;
class Test {
private static List<Object> test(List<List> list) {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<java.lang.Object>'">return list.stream().flatMap(List::stream).collect(Collectors.toList());</error>
return list.stream().flatMap(List::stream).<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<java.lang.Object>'">collect</error>(Collectors.toList());
}
private static List<Object> test1(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>
return list.stream().flatMap(l -> l.stream()).<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.util.List<java.lang.Object>'">collect</error>(Collectors.toList());
}
}
@@ -26,7 +26,7 @@ class C {
<error descr="Unqualified super reference is not allowed in extension method">super.m</error>();
System.out.println(<error descr="'C.I' is not an enclosing class">I.super</error>.i);
System.out.println(super.<error descr="Cannot resolve symbol 'i'">i</error>);
System.out.println(<error descr="Unqualified super reference is not allowed in extension method">super.<error descr="Cannot resolve symbol 'i'">i</error></error>);
}
void ma();
@@ -1,4 +1,4 @@
import static p.Foo.FooEx.<error descr="Static method may only be called on its containing interface">foo</error>;
import static <error descr="Static method may only be called on its containing interface">p.Foo.FooEx.foo;</error>
class FooImpl {
public void baz() {
@@ -12,9 +12,9 @@ interface Foo<T> {
class Bar {
Foo<List<String>> transform(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
return foo
.map(v2 -> tuple(v2))
.onClose();</error>
.<error descr="Incompatible types. Found: 'Foo<? extends java.util.List<? extends java.lang.String>>', required: 'Foo<java.util.List<java.lang.String>>'">onClose</error>();
}
static <T2> List<T2> tuple(T2 v2) {
@@ -1,4 +1,4 @@
// "Make 'call()' return 'Callable<java.lang.Integer>'" "true"
// "Make 'call()' return 'Callable<java.lang.Integer>' or ancestor" "true"
public class a extends Callable<Integer> {
public Callable<Integer> call() {
return new Callable<Integer>();
@@ -1,4 +1,4 @@
// "Make 'call()' return 'Callable<java.lang.Integer>'" "true"
// "Make 'call()' return 'Callable<java.lang.Integer>' or ancestor" "true"
public class a extends CallableEx<Integer> {
public Callable<Integer> call() {
return new Callable<Integer>();
@@ -1,4 +1,4 @@
// "Make 'f()' return 'java.lang.String'" "true-preview"
// "Make 'f()' return 'java.lang.String' or ancestor" "true-preview"
class a {
String f() {
return <caret>"";
@@ -1,4 +1,4 @@
// "Make 'call()' return 'java.lang.Integer'" "true"
// "Make 'call()' return 'java.lang.Integer' or ancestor" "true"
public class a {
String f() {
return new Callable<Integer>() {
@@ -1,4 +1,4 @@
// "Make 'call()' return 'java.lang.Integer'" "true"
// "Make 'call()' return 'java.lang.Integer' or ancestor" "true"
public class a implements Callable<Integer> {
public Integer call() {
return new Integer(0);
@@ -1,4 +1,4 @@
// "Make 'getDrawerAppsList()' return 'java.util.ArrayList<ResolveInfo>'" "true-preview"
// "Make 'getDrawerAppsList()' return 'java.util.ArrayList<ResolveInfo>' or ancestor" "true-preview"
import java.util.*;
class Test {
@@ -1,4 +1,4 @@
// "Make 'call()' return 'Callable<java.lang.Integer>'" "true"
// "Make 'call()' return 'Callable<java.lang.Integer>' or ancestor" "true"
public class a extends Callable<String> {
public Callable<String> call() {
return new Callable<Inte<caret>ger>();
@@ -1,4 +1,4 @@
// "Make 'call()' return 'Callable<java.lang.Integer>'" "true"
// "Make 'call()' return 'Callable<java.lang.Integer>' or ancestor" "true"
public class a extends CallableEx<String> {
public Callable<String> call() {
return new Callable<Inte<caret>ger>();
@@ -1,4 +1,4 @@
// "Make 'f()' return 'java.lang.String'" "true-preview"
// "Make 'f()' return 'java.lang.String' or ancestor" "true-preview"
class a {
int f() {
return <caret>"";
@@ -1,4 +1,4 @@
// "Make 'call()' return 'java.lang.Integer'" "true"
// "Make 'call()' return 'java.lang.Integer' or ancestor" "true"
public class a {
String f() {
return new Callable<String>() {
@@ -1,4 +1,4 @@
// "Make 'call()' return 'java.lang.Integer'" "true"
// "Make 'call()' return 'java.lang.Integer' or ancestor" "true"
public class a implements Callable<String> {
public String call() {
return new Int<caret>eger(0);
@@ -1,4 +1,4 @@
// "Make 'getDrawerAppsList()' return 'java.util.ArrayList<ResolveInfo>'" "true-preview"
// "Make 'getDrawerAppsList()' return 'java.util.ArrayList<ResolveInfo>' or ancestor" "true-preview"
import java.util.*;
class Test {
@@ -1,4 +1,4 @@
// "Make 'foo()' return 'java.util.List<java.lang.Integer>'" "true"
// "Make 'foo()' return 'java.util.List<java.lang.Integer>' or ancestor" "true"
import java.util.List;
interface Main<A, B> {
List<A> foo();
@@ -1,4 +1,4 @@
// "Make 'foo()' return 'java.util.List<java.lang.Integer>'" "true"
// "Make 'foo()' return 'java.util.List<java.lang.Integer>' or ancestor" "true"
import java.util.List;
interface Main<A, B> {
List<A> foo();
@@ -8,6 +8,6 @@ class ObjectEqualsToEquals {
<warning descr="'assertTrue()' can be simplified to 'assertArrayEquals()'"><caret>assertTrue</warning>(Arrays.equals(getFoo(), getBar()));
}
int[] getFoo() { <error descr="Incompatible types. Found: 'java.lang.String', required: 'int[]'">return "foo";</error> }
int[] getBar() { <error descr="Incompatible types. Found: 'java.lang.String', required: 'int[]'">return "foo";</error> }
int[] getFoo() { return <error descr="Incompatible types. Found: 'java.lang.String', required: 'int[]'">"foo";</error> }
int[] getBar() { return <error descr="Incompatible types. Found: 'java.lang.String', required: 'int[]'">"foo";</error> }
}