IDEA-141923 Errors in for-each statement header aren't highlighted if there're errors in its body

This commit is contained in:
Alexey Kudravtsev
2015-06-26 14:17:06 +03:00
parent 5465f1cc88
commit 9e99eabbb6
8 changed files with 81 additions and 68 deletions

View File

@@ -689,10 +689,7 @@ public class GenericsHighlightUtil {
return null;
}
@Nullable
public static HighlightInfo checkForeachLoopParameterType(PsiForeachStatement statement) {
final PsiParameter parameter = statement.getIterationParameter();
final PsiExpression expression = statement.getIteratedValue();
static HighlightInfo checkForeachExpressionTypeIsIterable(PsiExpression expression) {
if (expression == null || expression.getType() == null) return null;
final PsiType itemType = JavaGenericsUtil.getCollectionItemType(expression);
if (itemType == null) {
@@ -700,13 +697,20 @@ public class GenericsHighlightUtil {
JavaHighlightUtil.formatType(expression.getType()));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create();
}
final int start = parameter.getTextRange().getStartOffset();
final int end = expression.getTextRange().getEndOffset();
return null;
}
static HighlightInfo checkForEachParameterType(@NotNull PsiForeachStatement statement, @NotNull PsiParameter parameter) {
final PsiExpression expression = statement.getIteratedValue();
final PsiType itemType = expression == null ? null : JavaGenericsUtil.getCollectionItemType(expression);
if (itemType == null) return null;
final PsiType parameterType = parameter.getType();
HighlightInfo highlightInfo = HighlightUtil.checkAssignability(parameterType, itemType, null, new TextRange(start, end), 0);
if (highlightInfo != null) {
HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, expression, highlightInfo);
if (TypeConversionUtil.isAssignable(parameterType, itemType)) {
return null;
}
HighlightInfo highlightInfo = HighlightUtil.createIncompatibleTypeHighlightInfo(itemType, parameterType, parameter.getTextRange(), 0);
HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, expression, highlightInfo);
return highlightInfo;
}

View File

@@ -172,7 +172,7 @@ public class HighlightUtil extends HighlightUtilBase {
if (modifierList.hasModifierProperty(incompatible)) {
return incompatible;
}
else if (PsiModifier.ABSTRACT.equals(incompatible) && modifierList.hasExplicitModifier(incompatible)) {
if (PsiModifier.ABSTRACT.equals(incompatible) && modifierList.hasExplicitModifier(incompatible)) {
return incompatible;
}
}
@@ -377,7 +377,7 @@ public class HighlightUtil extends HighlightUtilBase {
if (castType instanceof PsiIntersectionType) return true;
return castType instanceof PsiClassType && PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class) != null;
}
@Nullable
static HighlightInfo checkInconvertibleTypeCast(@NotNull PsiTypeCastExpression expression) {
final PsiTypeElement castTypeElement = expression.getCastType();
@@ -396,7 +396,7 @@ public class HighlightUtil extends HighlightUtilBase {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message).create();
}
return null;
}
@@ -485,12 +485,12 @@ public class HighlightUtil extends HighlightUtilBase {
private static void registerChangeVariableTypeFixes(@NotNull PsiExpression expression,
@NotNull PsiType type,
@Nullable PsiExpression lExpr,
@Nullable PsiExpression lExpr,
@Nullable HighlightInfo highlightInfo) {
if (highlightInfo == null || !(expression instanceof PsiReferenceExpression)) return;
final PsiElement element = ((PsiReferenceExpression)expression).resolve();
if (element == null || !(element instanceof PsiVariable)) return;
if (!(element instanceof PsiVariable)) return;
registerChangeVariableTypeFixes((PsiVariable)element, type, lExpr, highlightInfo);
}
@@ -528,17 +528,20 @@ public class HighlightUtil extends HighlightUtilBase {
}
@Nullable
static HighlightInfo checkAssignability(PsiType lType, @Nullable PsiType rType, PsiExpression expression, @NotNull PsiElement elementToHighlight) {
static HighlightInfo checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull PsiElement elementToHighlight) {
TextRange textRange = elementToHighlight.getTextRange();
return checkAssignability(lType, rType, expression, textRange, 0);
}
@Nullable
static HighlightInfo checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull TextRange textRange,
int navigationShift) {
private static HighlightInfo checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull TextRange textRange,
int navigationShift) {
if (lType == rType) return null;
if (expression == null) {
if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return null;
@@ -657,8 +660,8 @@ public class HighlightUtil extends HighlightUtilBase {
if (variable instanceof PsiLocalVariable ||
variable instanceof PsiParameter &&
((declarationScope = ((PsiParameter)variable).getDeclarationScope()) instanceof PsiCatchSection ||
declarationScope instanceof PsiForeachStatement ||
declarationScope instanceof PsiLambdaExpression)) {
declarationScope instanceof PsiForeachStatement ||
declarationScope instanceof PsiLambdaExpression)) {
@SuppressWarnings("unchecked")
PsiElement scope = PsiTreeUtil.getParentOfType(variable, PsiFile.class, PsiMethod.class, PsiClassInitializer.class, PsiResourceList.class);
VariablesNotProcessor proc = new VariablesNotProcessor(variable, false) {
@@ -930,7 +933,7 @@ public class HighlightUtil extends HighlightUtilBase {
}
if (PsiModifier.PRIVATE.equals(modifier)) {
isAllowed &= modifierOwnerParent instanceof PsiClass &&
isAllowed &= modifierOwnerParent instanceof PsiClass &&
(!((PsiClass)modifierOwnerParent).isInterface() || PsiUtil.isLanguageLevel9OrHigher(modifierOwner));
}
else if (PsiModifier.STRICTFP.equals(modifier)) {
@@ -1254,8 +1257,8 @@ public class HighlightUtil extends HighlightUtilBase {
@Nullable
private static HighlightInfo checkSimpleCatchParameter(@NotNull final PsiParameter parameter,
@NotNull final Collection<PsiClassType> thrownTypes,
@NotNull final PsiClassType caughtType) {
@NotNull final Collection<PsiClassType> thrownTypes,
@NotNull final PsiClassType caughtType) {
if (ExceptionUtil.isUncheckedExceptionOrSuperclass(caughtType)) return null;
for (PsiClassType exceptionType : thrownTypes) {
@@ -1794,7 +1797,7 @@ public class HighlightUtil extends HighlightUtilBase {
PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
if (switchStatement == null) {
String description = JavaErrorMessages.message("case.statement.outside.switch");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description).create();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(description).create();
}
if (switchStatement.getBody() == null) return null;
PsiExpression switchExpression = switchStatement.getExpression();
@@ -1816,9 +1819,9 @@ public class HighlightUtil extends HighlightUtilBase {
if (element instanceof PsiEnumConstant) {
isEnumSwitch = true;
value = ((PsiEnumConstant)element).getName();
if (!(((PsiReferenceExpression)caseValue).getQualifier() == null)) {
if (((PsiReferenceExpression)caseValue).getQualifier() != null) {
String message = JavaErrorMessages.message("qualified.enum.constant.in.switch");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(caseValue).descriptionAndTooltip(message).create();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(caseValue).descriptionAndTooltip(message).create();
}
}
}
@@ -1827,7 +1830,7 @@ public class HighlightUtil extends HighlightUtilBase {
}
if (value == null) {
String description = JavaErrorMessages.message("constant.expression.required");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(caseValue).descriptionAndTooltip(description).create();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(caseValue).descriptionAndTooltip(description).create();
}
}
@@ -1852,7 +1855,7 @@ public class HighlightUtil extends HighlightUtilBase {
? JavaErrorMessages.message("duplicate.default.switch.label")
: JavaErrorMessages.message("duplicate.switch.label", value);
PsiElement element = value == null ? statement : caseValue;
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
}
// must be followed with colon
@@ -2262,7 +2265,7 @@ public class HighlightUtil extends HighlightUtilBase {
@Nullable
static HighlightInfo checkUnclosedComment(@NotNull PsiComment comment) {
if (!(comment instanceof PsiDocComment) && !(comment.getTokenType() == JavaTokenType.C_STYLE_COMMENT)) return null;
if (!(comment instanceof PsiDocComment) && comment.getTokenType() != JavaTokenType.C_STYLE_COMMENT) return null;
if (!comment.getText().endsWith("*/")) {
int start = comment.getTextRange().getEndOffset() - 1;
int end = start + 1;
@@ -2442,10 +2445,10 @@ public class HighlightUtil extends HighlightUtilBase {
return null;
}
private static HighlightInfo createIncompatibleTypeHighlightInfo(final PsiType lType,
final PsiType rType,
@NotNull final TextRange textRange,
int navigationShift) {
static HighlightInfo createIncompatibleTypeHighlightInfo(final PsiType lType,
final PsiType rType,
@NotNull final TextRange textRange,
int navigationShift) {
PsiType lType1 = lType;
PsiType rType1 = rType;
PsiTypeParameter[] lTypeParams = PsiTypeParameter.EMPTY_ARRAY;
@@ -2852,9 +2855,9 @@ public class HighlightUtil extends HighlightUtilBase {
}
private static void registerReplaceInaccessibleFieldWithGetterSetterFix(PsiMember refElement,
PsiJavaCodeReferenceElement place,
PsiClass accessObjectClass,
HighlightInfo error) {
PsiJavaCodeReferenceElement place,
PsiClass accessObjectClass,
HighlightInfo error) {
if (refElement instanceof PsiField && place instanceof PsiReferenceExpression) {
final PsiField psiField = (PsiField)refElement;
final PsiClass containingClass = psiField.getContainingClass();

View File

@@ -512,9 +512,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkValidArrayAccessExpression((PsiArrayAccessExpression)expression));
}
if (expression.getParent() instanceof PsiNewExpression
&& ((PsiNewExpression)expression.getParent()).getQualifier() != expression
&& ((PsiNewExpression)expression.getParent()).getArrayInitializer() != expression) {
PsiElement parent = expression.getParent();
if (parent instanceof PsiNewExpression
&& ((PsiNewExpression)parent).getQualifier() != expression
&& ((PsiNewExpression)parent).getArrayInitializer() != expression) {
// like in 'new String["s"]'
myHolder.add(HighlightUtil.checkAssignability(PsiType.INT, expression.getType(), expression, expression));
}
@@ -523,17 +524,20 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkArrayInitializer(expression, type));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkTernaryOperatorConditionIsBoolean(expression, type));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkAssertOperatorTypes(expression, type));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSynchronizedExpressionType(expression, type,myFile));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSynchronizedExpressionType(expression, type, myFile));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkConditionalExpressionBranchTypesMatch(expression, type));
if (!myHolder.hasErrorResults()
&& expression.getParent() instanceof PsiThrowStatement
&& ((PsiThrowStatement)expression.getParent()).getException() == expression) {
&& parent instanceof PsiThrowStatement
&& ((PsiThrowStatement)parent).getException() == expression) {
myHolder.add(HighlightUtil.checkMustBeThrowable(type, expression, true));
}
if (!myHolder.hasErrorResults()) {
myHolder.add(AnnotationsHighlightUtil.checkConstantExpression(expression));
}
if (!myHolder.hasErrorResults() && parent instanceof PsiForeachStatement && ((PsiForeachStatement)parent).getIteratedValue() == expression) {
myHolder.add(GenericsHighlightUtil.checkForeachExpressionTypeIsIterable(expression));
}
}
@Override
@@ -585,7 +589,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
myHolder.add(checkFeature(statement, Feature.FOR_EACH));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkForeachLoopParameterType(statement));
}
@Override
@@ -982,6 +985,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!myHolder.hasErrorResults()) myHolder.addAll(GenericsHighlightUtil.checkCatchParameterIsClass(parameter));
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkCatchTypeIsDisjoint(parameter));
}
else if (parent instanceof PsiForeachStatement) {
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkForEachParameterType((PsiForeachStatement)parent, parameter));
}
}
@Override
@@ -1149,8 +1155,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private JavaResolveResult resolveOptimised(@NotNull PsiJavaCodeReferenceElement ref) {
JavaResolveResult result;
if (ref instanceof PsiReferenceExpressionImpl) {
PsiReferenceExpressionImpl referenceExpression = (PsiReferenceExpressionImpl)ref;
JavaResolveResult[] results = JavaResolveUtil.resolveWithContainingFile(referenceExpression,
JavaResolveResult[] results = JavaResolveUtil.resolveWithContainingFile(ref,
PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE,
true, true,
myFile);
@@ -1166,8 +1171,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private JavaResolveResult[] resolveOptimised(@NotNull PsiReferenceExpression expression) {
JavaResolveResult[] results;
if (expression instanceof PsiReferenceExpressionImpl) {
PsiReferenceExpressionImpl referenceExpression = (PsiReferenceExpressionImpl)expression;
results = JavaResolveUtil.resolveWithContainingFile(referenceExpression,
results = JavaResolveUtil.resolveWithContainingFile(expression,
PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE, true, true,
myFile);
}

View File

@@ -13,7 +13,7 @@ class UnsupportedFeatures {
List<error descr="Generics are not supported at this language level"><String></error> list =
new ArrayList<error descr="Generics are not supported at this language level"><></error>();
<error descr="For-each loops are not supported at this language level">for (String s : list) {}</error>
<error descr="For-each loops are not supported at this language level">for (Object s : list) {}</error>
Arrays.asList<error descr="'asList(java.lang.String...)' in 'java.util.Arrays' cannot be applied to '(java.lang.String)'">("")</error>;
<error descr="Incompatible types. Found: 'boolean', required: 'java.lang.Boolean'">Boolean b = true;</error>
<error descr="Incompatible types. Found: 'java.lang.Boolean', required: 'boolean'">boolean b1 = Boolean.TRUE;</error>

View File

@@ -23,7 +23,7 @@ public class DefaultEventSource<<warning descr="Type parameter 'T' is never used
final Iterator<String> <warning descr="Variable 'keys2' is assigned but never accessed">keys2</warning>;
keys2 = <warning descr="Unchecked assignment: 'java.util.Iterator' to 'java.util.Iterator<java.lang.String>'. Reason: 'source' has raw type, so result of getKeys is erased">source.getKeys()</warning>;
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String <warning descr="Parameter 'o' is never used">o</warning> : super.getList()</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.Object'">String <warning descr="Parameter 'o' is never used">o</warning></error> : super.getList()) {}
}
}

View File

@@ -1,30 +1,33 @@
class a {
void f(int[] c) {
for (int i:c) {}
for (<error descr="Incompatible types. Found: 'int', required: 'char'">char i:c</error>) {}
for (<error descr="Incompatible types. Found: 'char', required: 'int'">char i</error>:c) {}
for (double i:c) {}
double[] db = null;
for (<error descr="Incompatible types. Found: 'double', required: 'int'">int i:db</error>) {}
for (<error descr="Incompatible types. Found: 'int', required: 'double'">int i</error>:db) {}
for (double i:db) {}
for (<error descr="Incompatible types. Found: 'int', required: 'double'">int i</error>:db) {
// highlight header event if body has problems
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int di = "";</error>
}
java.util.List list = null;
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String i:list</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.Object'">String i</error>:list) {}
for (Object o:list) {}
java.util.List<Integer> ct = null;
for (Number n:ct) {}
for (Object n:ct) {}
for (Integer n:ct) {}
for (<error descr="Incompatible types. Found: 'java.lang.Integer', required: 'java.lang.String'">String i:ct</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.Integer', required: 'java.util.List<java.lang.Integer>'">java.util.List<Integer> i:ct</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.Integer'">String i</error>:ct) {}
for (<error descr="Incompatible types. Found: 'java.util.List<java.lang.Integer>', required: 'java.lang.Integer'">java.util.List<Integer> i</error>:ct) {}
Object o = null;
for (Object oi: (Iterable)o) {}
// error descr="Incompatible types. Found: 'double', required: 'int'" was not shown because there is an error in nested for
for (int i : db) {
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'int'">int p: list</error>) {}
for (<error descr="Incompatible types. Found: 'int', required: 'double'">int i</error> : db) {
for (<error descr="Incompatible types. Found: 'int', required: 'java.lang.Object'">int p</error>: list) {}
}
for (int gjkh : <error descr="foreach not applicable to type 'int'">222</error>) {

View File

@@ -1,9 +1,8 @@
interface Container<T extends Content> extends Iterable<T> {}
interface Content {}
class Main {
public static void doSomething(Container container) {
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'Content'">Content content : container</error>) {}
for (<error descr="Incompatible types. Found: 'Content', required: 'java.lang.Object'">Content content</error> : container) {}
}
}

View File

@@ -1,29 +1,29 @@
class a {
void f(int[] c) {
for (int i:c) {}
for (<error descr="Incompatible types. Found: 'int', required: 'char'">char i:c</error>) {}
for (<error descr="Incompatible types. Found: 'char', required: 'int'">char i</error>:c) {}
for (double i:c) {}
double[] db = null;
for (<error descr="Incompatible types. Found: 'double', required: 'int'">int i:db</error>) {}
for (<error descr="Incompatible types. Found: 'int', required: 'double'">int i</error>:db) {}
for (double i:db) {}
java.util.List list = null;
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String i:list</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.Object'">String i</error>:list) {}
for (Object o:list) {}
java.util.List<Integer> ct = null;
for (Number n:ct) {}
for (Object n:ct) {}
for (Integer n:ct) {}
for (<error descr="Incompatible types. Found: 'java.lang.Integer', required: 'java.lang.String'">String i:ct</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.Integer', required: 'java.util.List<java.lang.Integer>'">java.util.List<Integer> i:ct</error>) {}
for (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.Integer'">String i</error>:ct) {}
for (<error descr="Incompatible types. Found: 'java.util.List<java.lang.Integer>', required: 'java.lang.Integer'">java.util.List<Integer> i</error>:ct) {}
Object o = null;
for (Object oi: (Iterable)o) {}
for (int i:db) {
for (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'int'">int p: list</error>) {}
for (<error descr="Incompatible types. Found: 'int', required: 'double'">int i</error>:db) {
for (<error descr="Incompatible types. Found: 'int', required: 'java.lang.Object'">int p</error>: list) {}
}
}
}