[java] improves "unsupported language level" message (IDEA-168859)

This commit is contained in:
Roman Shevchenko
2017-03-06 11:26:41 +01:00
parent f445fe460d
commit 74daf8d522
15 changed files with 252 additions and 214 deletions

View File

@@ -40,6 +40,7 @@ import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.ui.UIUtil;
@@ -50,9 +51,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
/**
* Highlight method problems
@@ -344,7 +343,8 @@ public class HighlightMethodUtil {
static HighlightInfo checkMethodCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull LanguageLevel languageLevel,
@NotNull JavaSdkVersion javaSdkVersion) {
@NotNull JavaSdkVersion javaSdkVersion,
@NotNull PsiFile file) {
PsiExpressionList list = methodCall.getArgumentList();
PsiReferenceExpression referenceToMethod = methodCall.getMethodExpression();
JavaResolveResult[] results = referenceToMethod.multiResolve(true);
@@ -359,34 +359,40 @@ public class HighlightMethodUtil {
if (resolved instanceof PsiMethod && resolveResult.isValidResult()) {
TextRange fixRange = getFixRange(methodCall);
highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
if (highlightInfo == null) {
String invalidCallMessage =
LambdaUtil.getInvalidQualifier4StaticInterfaceMethodMessage((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel);
if (invalidCallMessage != null) {
invalidCallMessage = HighlightUtil.extendUnsupportedLanguageLevelDescription(invalidCallMessage, methodCall, languageLevel, methodCall.getContainingFile(), LanguageLevel.JDK_1_8);
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(invalidCallMessage).range(fixRange).create();
if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_8));
}
} else {
highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
}
if (highlightInfo == null) {
highlightInfo = checkVarargParameterErasureToBeAccessible((MethodCandidateInfo)resolveResult, methodCall);
}
if (highlightInfo == null) {
final String errorMessage = ((MethodCandidateInfo)resolveResult).getInferenceErrorMessage();
if (errorMessage != null) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(errorMessage).range(fixRange).create();
if (highlightInfo != null) {
registerMethodCallIntentions(highlightInfo, methodCall, list, resolveHelper);
registerMethodReturnFixAction(highlightInfo, (MethodCandidateInfo)resolveResult, methodCall);
if (highlightInfo == null && ((PsiMethod)resolved).hasModifierProperty(PsiModifier.STATIC)) {
PsiClass containingClass = ((PsiMethod)resolved).getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
PsiReferenceExpression methodRef = methodCall.getMethodExpression();
PsiElement element = ObjectUtils.notNull(methodRef.getReferenceNameElement(), methodRef);
highlightInfo = HighlightUtil.checkFeature(element, HighlightUtil.Feature.STATIC_INTERFACE_CALLS, languageLevel, file);
if (highlightInfo == null) {
String message = checkStaticInterfaceMethodCallQualifier(methodRef, resolveResult.getCurrentFileResolveScope(), containingClass);
if (message != null) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(fixRange).create();
}
}
}
}
if (highlightInfo == null) {
highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
}
if (highlightInfo == null) {
highlightInfo = checkVarargParameterErasureToBeAccessible((MethodCandidateInfo)resolveResult, methodCall);
}
if (highlightInfo == null) {
String errorMessage = ((MethodCandidateInfo)resolveResult).getInferenceErrorMessage();
if (errorMessage != null) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(errorMessage).range(fixRange).create();
if (highlightInfo != null) {
registerMethodCallIntentions(highlightInfo, methodCall, list, resolveHelper);
registerMethodReturnFixAction(highlightInfo, (MethodCandidateInfo)resolveResult, methodCall);
}
}
}
}
else {
PsiMethod resolvedMethod = null;
@@ -460,12 +466,43 @@ public class HighlightMethodUtil {
}
}
if (highlightInfo == null) {
highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor,
javaSdkVersion);
highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor, javaSdkVersion);
}
return highlightInfo;
}
/* see also PsiReferenceExpressionImpl.hasValidQualifier() */
@Nullable
private static String checkStaticInterfaceMethodCallQualifier(PsiReferenceExpression ref, PsiElement scope, PsiClass containingClass) {
PsiExpression qualifierExpression = ref.getQualifierExpression();
if (qualifierExpression == null && (scope instanceof PsiImportStaticStatement || PsiTreeUtil.isAncestor(containingClass, ref, true))) {
return null;
}
if (qualifierExpression instanceof PsiReferenceExpression) {
PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
if (resolve == containingClass) {
return null;
}
if (resolve instanceof PsiTypeParameter) {
Set<PsiClass> classes = new HashSet<>();
for (PsiClassType type : ((PsiTypeParameter)resolve).getExtendsListTypes()) {
PsiClass aClass = type.resolve();
if (aClass != null) {
classes.add(aClass);
}
}
if (classes.size() == 1 && classes.contains(containingClass)) {
return null;
}
}
}
return JavaErrorMessages.message("static.interface.method.call.qualifier");
}
private static void registerMethodReturnFixAction(HighlightInfo highlightInfo,
MethodCandidateInfo candidate,
PsiCall methodCall) {
@@ -557,12 +594,14 @@ public class HighlightMethodUtil {
@Nullable
static HighlightInfo checkAmbiguousMethodCallIdentifier(@NotNull PsiReferenceExpression referenceToMethod,
@NotNull JavaResolveResult[] resolveResults,
@NotNull PsiExpressionList list,
final PsiElement element,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper) {
@NotNull JavaResolveResult[] resolveResults,
@NotNull PsiExpressionList list,
final PsiElement element,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull LanguageLevel languageLevel,
@NotNull PsiFile file) {
MethodCandidateInfo methodCandidate1 = null;
MethodCandidateInfo methodCandidate2 = null;
for (JavaResolveResult result : resolveResults) {
@@ -591,19 +630,21 @@ public class HighlightMethodUtil {
elementToHighlight = referenceToMethod.getReferenceNameElement();
}
else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(referenceToMethod);
final String staticInterfaceMethodMessage =
element instanceof PsiMethod
? LambdaUtil.getInvalidQualifier4StaticInterfaceMethodMessage((PsiMethod)element, referenceToMethod,
resolveResult.getCurrentFileResolveScope(), languageLevel)
: null;
if (staticInterfaceMethodMessage != null) {
description = HighlightUtil.extendUnsupportedLanguageLevelDescription(staticInterfaceMethodMessage, methodCall, languageLevel, referenceToMethod.getContainingFile(), LanguageLevel.JDK_1_8);
description = null;
elementToHighlight = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod);
if (element instanceof PsiMethod && ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC)) {
PsiClass containingClass = ((PsiMethod)element).getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
HighlightInfo info = HighlightUtil.checkFeature(elementToHighlight, HighlightUtil.Feature.STATIC_INTERFACE_CALLS, languageLevel, file);
if (info != null) return info;
description = checkStaticInterfaceMethodCallQualifier(referenceToMethod, resolveResult.getCurrentFileResolveScope(), containingClass);
}
}
else {
if (description == null) {
description = HighlightUtil.buildProblemWithStaticDescription(element);
}
elementToHighlight = referenceToMethod.getReferenceNameElement();
}
else {
String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
@@ -616,6 +657,7 @@ public class HighlightMethodUtil {
return null;
}
}
String toolTip = XmlStringUtil.escapeString(description);
HighlightInfo info =
HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();

View File

@@ -301,72 +301,69 @@ public class HighlightUtil extends HighlightUtilBase {
* ( ReferenceType {AdditionalBound} ) expression, where AdditionalBound: & InterfaceType then all must be true
* • ReferenceType must denote a class or interface type.
* • The erasures of all the listed types must be pairwise different.
* • No two listed types may be subtypes of different parameterizations of the same generic interface.
* • No two listed types may be subtypes of different parameterization of the same generic interface.
*/
@Nullable
static HighlightInfo checkIntersectionInTypeCast(@NotNull PsiTypeCastExpression expression, @NotNull LanguageLevel languageLevel) {
final PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null) return null;
PsiType castType = castTypeElement.getType();
if (isIntersection(castTypeElement, castType)) {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiTypeElement[] conjuncts = PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class);
if (conjuncts != null) {
final Set<PsiType> erasures = new HashSet<>(conjuncts.length);
erasures.add(TypeConversionUtil.erasure(conjuncts[0].getType()));
final List<PsiTypeElement> conjList = new ArrayList<>(Arrays.asList(conjuncts));
for (int i = 1; i < conjuncts.length; i++) {
final PsiTypeElement conjunct = conjuncts[i];
final PsiType conjType = conjunct.getType();
if (conjType instanceof PsiClassType) {
final PsiClass aClass = ((PsiClassType)conjType).resolve();
if (aClass != null && !aClass.isInterface()) {
final HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip(JavaErrorMessages.message("interface.expected")).create();
QuickFixAction.registerQuickFixAction(errorResult, new FlipIntersectionSidesFix(aClass.getName(), conjList, conjunct, castTypeElement), null);
return errorResult;
}
}
else {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
static HighlightInfo checkIntersectionInTypeCast(@NotNull PsiTypeCastExpression expression,
@NotNull LanguageLevel languageLevel,
@NotNull PsiFile file) {
PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement != null && isIntersection(castTypeElement, castTypeElement.getType())) {
HighlightInfo info = checkFeature(expression, Feature.INTERSECTION_CASTS, languageLevel, file);
if (info != null) return info;
final PsiTypeElement[] conjuncts = PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class);
if (conjuncts != null) {
final Set<PsiType> erasures = new HashSet<>(conjuncts.length);
erasures.add(TypeConversionUtil.erasure(conjuncts[0].getType()));
final List<PsiTypeElement> conjList = new ArrayList<>(Arrays.asList(conjuncts));
for (int i = 1; i < conjuncts.length; i++) {
final PsiTypeElement conjunct = conjuncts[i];
final PsiType conjType = conjunct.getType();
if (conjType instanceof PsiClassType) {
final PsiClass aClass = ((PsiClassType)conjType).resolve();
if (aClass != null && !aClass.isInterface()) {
final HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip("Unexpected type: class is expected").create();
}
if (!erasures.add(TypeConversionUtil.erasure(conjType))) {
final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip("Repeated interface").create();
QuickFixAction.registerQuickFixAction(highlightInfo, new DeleteRepeatedInterfaceFix(conjunct, conjList), null);
return highlightInfo;
.descriptionAndTooltip(JavaErrorMessages.message("interface.expected")).create();
QuickFixAction.registerQuickFixAction(errorResult, new FlipIntersectionSidesFix(aClass.getName(), conjList, conjunct, castTypeElement), null);
return errorResult;
}
}
final List<PsiType> typeList = ContainerUtil.map(conjList, PsiTypeElement::getType);
final Ref<String> differentArgumentsMessage = new Ref<>();
final PsiClass sameGenericParameterization =
InferenceSession.findParameterizationOfTheSameGenericClass(typeList, pair -> {
if (!TypesDistinctProver.provablyDistinct(pair.first, pair.second)) {
return true;
}
differentArgumentsMessage.set(pair.first.getPresentableText() + " and " + pair.second.getPresentableText());
return false;
});
if (sameGenericParameterization != null) {
final String message = formatClass(sameGenericParameterization) + " cannot be inherited with different arguments: " +
differentArgumentsMessage.get();
else {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(expression)
.descriptionAndTooltip(message).create();
.range(conjunct)
.descriptionAndTooltip("Unexpected type: class is expected").create();
}
if (!erasures.add(TypeConversionUtil.erasure(conjType))) {
final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip("Repeated interface").create();
QuickFixAction.registerQuickFixAction(highlightInfo, new DeleteRepeatedInterfaceFix(conjunct, conjList), null);
return highlightInfo;
}
}
} else {
String description = extendUnsupportedLanguageLevelDescription("Intersection types in cast are not supported at this language level", expression, languageLevel, expression.getContainingFile(), LanguageLevel.JDK_1_8);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(expression)
.descriptionAndTooltip(description).create();
final List<PsiType> typeList = ContainerUtil.map(conjList, PsiTypeElement::getType);
final Ref<String> differentArgumentsMessage = new Ref<>();
final PsiClass sameGenericParameterization =
InferenceSession.findParameterizationOfTheSameGenericClass(typeList, pair -> {
if (!TypesDistinctProver.provablyDistinct(pair.first, pair.second)) {
return true;
}
differentArgumentsMessage.set(pair.first.getPresentableText() + " and " + pair.second.getPresentableText());
return false;
});
if (sameGenericParameterization != null) {
final String message = formatClass(sameGenericParameterization) + " cannot be inherited with different arguments: " +
differentArgumentsMessage.get();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(expression)
.descriptionAndTooltip(message).create();
}
}
}
return null;
}
@@ -3003,13 +3000,15 @@ public class HighlightUtil extends HighlightUtilBase {
LAMBDA_EXPRESSIONS(LanguageLevel.JDK_1_8, "feature.lambda.expressions"),
TYPE_ANNOTATIONS(LanguageLevel.JDK_1_8, "feature.type.annotations"),
RECEIVERS(LanguageLevel.JDK_1_8, "feature.type.receivers"),
INTERSECTION_CASTS(LanguageLevel.JDK_1_8, "feature.intersections.in.casts"),
STATIC_INTERFACE_CALLS(LanguageLevel.JDK_1_8, "feature.static.interface.calls"),
REFS_AS_RESOURCE(LanguageLevel.JDK_1_9, "feature.try.with.resources.refs"),
MODULES(LanguageLevel.JDK_1_9, "feature.modules");
private final LanguageLevel level;
private final String key;
Feature(@NotNull LanguageLevel level, @NotNull @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String key) {
Feature(LanguageLevel level, @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String key) {
this.level = level;
this.key = key;
}
@@ -3021,9 +3020,7 @@ public class HighlightUtil extends HighlightUtilBase {
@NotNull LanguageLevel level,
@NotNull PsiFile file) {
if (file.getManager().isInProject(file) && !level.isAtLeast(feature.level)) {
String message = JavaErrorMessages.message("insufficient.language.level", JavaErrorMessages.message(feature.key));
message = extendUnsupportedLanguageLevelDescription(message, element, level, file, feature.level);
String message = getUnsupportedFeatureMessage(element, feature, level, file);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(message).create();
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(feature.level));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createShowModulePropertiesFix(element));
@@ -3033,18 +3030,17 @@ public class HighlightUtil extends HighlightUtilBase {
return null;
}
static String extendUnsupportedLanguageLevelDescription(@NotNull String message,
@NotNull PsiElement element,
@NotNull LanguageLevel fileLanguageLevel,
@NotNull PsiFile file,
@NotNull LanguageLevel featureLevel) {
private static String getUnsupportedFeatureMessage(PsiElement element, Feature feature, LanguageLevel level, PsiFile file) {
String name = JavaErrorMessages.message(feature.key);
String message = JavaErrorMessages.message("insufficient.language.level", name, level.getCompilerComplianceDefaultOption());
Module module = ModuleUtilCore.findModuleForPsiElement(element);
if (module != null) {
LanguageLevel moduleLanguageLevel = EffectiveLanguageLevelUtil.getEffectiveLanguageLevel(module);
if (moduleLanguageLevel.isAtLeast(featureLevel)) {
if (moduleLanguageLevel.isAtLeast(feature.level)) {
for (FilePropertyPusher pusher : FilePropertyPusher.EP_NAME.getExtensions()) {
if (pusher instanceof JavaLanguageLevelPusher) {
String newMessage = ((JavaLanguageLevelPusher)pusher).getInconsistencyLanguageLevelMessage(message, element, fileLanguageLevel, file);
String newMessage = ((JavaLanguageLevelPusher)pusher).getInconsistencyLanguageLevelMessage(message, element, level, file);
if (newMessage != null) {
return newMessage;
}
@@ -3052,6 +3048,7 @@ public class HighlightUtil extends HighlightUtilBase {
}
}
}
return message;
}
}

View File

@@ -879,7 +879,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
// in case of JSP synthetic method call, do not check
if (myFile.isPhysical() && !myHolder.hasErrorResults()) {
try {
myHolder.add(HighlightMethodUtil.checkMethodCall(expression, myResolveHelper, myLanguageLevel,myJavaSdkVersion));
myHolder.add(HighlightMethodUtil.checkMethodCall(expression, myResolveHelper, myLanguageLevel, myJavaSdkVersion, myFile));
}
catch (IndexNotReadyException ignored) { }
}
@@ -1257,7 +1257,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!HighlightMethodUtil.isDummyConstructorCall(methodCallExpression, myResolveHelper, list, expression)) {
try {
myHolder.add(HighlightMethodUtil.checkAmbiguousMethodCallIdentifier(
expression, results, list, resolved, result, methodCallExpression, myResolveHelper));
expression, results, list, resolved, result, methodCallExpression, myResolveHelper, myLanguageLevel, myFile));
if (!PsiTreeUtil.findChildrenOfType(methodCallExpression.getArgumentList(), PsiLambdaExpression.class).isEmpty()) {
PsiElement nameElement = expression.getReferenceNameElement();
@@ -1596,7 +1596,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
super.visitTypeCastExpression(typeCast);
try {
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkIntersectionInTypeCast(typeCast, myLanguageLevel));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkIntersectionInTypeCast(typeCast, myLanguageLevel, myFile));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkInconvertibleTypeCast(typeCast));
}
catch (IndexNotReadyException ignored) { }

View File

@@ -19,7 +19,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
@@ -35,8 +34,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* User: anna
* Date: 7/17/12
* @author anna
* @since 17.07.2012
*/
public class LambdaUtil {
public static final RecursionGuard ourParameterGuard = RecursionManager.createGuard("lambdaParameterGuard");
@@ -582,51 +581,6 @@ public class LambdaUtil {
return result;
}
public static boolean isValidQualifier4InterfaceStaticMethodCall(@NotNull PsiMethod method,
@NotNull PsiReferenceExpression methodReferenceExpression,
@Nullable PsiElement scope, @NotNull LanguageLevel languageLevel) {
return getInvalidQualifier4StaticInterfaceMethodMessage(method, methodReferenceExpression, scope, languageLevel) == null;
}
@Nullable
public static String getInvalidQualifier4StaticInterfaceMethodMessage(@NotNull PsiMethod method,
@NotNull PsiReferenceExpression methodReferenceExpression,
@Nullable PsiElement scope, @NotNull LanguageLevel languageLevel) {
final PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression();
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface() && method.hasModifierProperty(PsiModifier.STATIC)) {
if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
return "Static interface method invocations are not supported at this language level";
}
if (qualifierExpression == null && (scope instanceof PsiImportStaticStatement || PsiTreeUtil.isAncestor(containingClass, methodReferenceExpression, true))) {
return null;
}
if (qualifierExpression instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
if (resolve == containingClass) {
return null;
}
if (resolve instanceof PsiTypeParameter) {
final Set<PsiClass> classes = new HashSet<>();
for (PsiClassType type : ((PsiTypeParameter)resolve).getExtendsListTypes()) {
final PsiClass aClass = type.resolve();
if (aClass != null) {
classes.add(aClass);
}
}
if (classes.size() == 1 && classes.contains(containingClass)) {
return null;
}
}
}
return "Static method may be invoked on containing interface class only";
}
return null;
}
//JLS 14.8 Expression Statements
@Contract("null -> false")
public static boolean isExpressionStatementExpression(PsiElement body) {
@@ -1029,4 +983,4 @@ public class LambdaUtil {
return false;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -52,10 +51,7 @@ import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiReferenceExpression, SourceJavaCodeReference {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl");
@@ -491,8 +487,7 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
private boolean shouldProcessMethod(@NotNull PsiMethod method) {
PsiReferenceExpressionImpl ref = PsiReferenceExpressionImpl.this;
return !method.isConstructor() &&
LambdaUtil.isValidQualifier4InterfaceStaticMethodCall(method, ref, myResolveContext, PsiUtil.getLanguageLevel(ref));
return !method.isConstructor() && hasValidQualifier(method, ref, myResolveContext);
}
@Override
@@ -507,6 +502,46 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
PsiScopesUtil.resolveAndWalk(filterProcessor, this, null, true);
}
/* see also HighlightMethodUtil.checkStaticInterfaceMethodCallQualifier() */
private static boolean hasValidQualifier(PsiMethod method, PsiReferenceExpression ref, PsiElement scope) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface() && method.hasModifierProperty(PsiModifier.STATIC)) {
if (!PsiUtil.getLanguageLevel(ref).isAtLeast(LanguageLevel.JDK_1_8)) {
return false;
}
PsiExpression qualifierExpression = ref.getQualifierExpression();
if (qualifierExpression == null && (scope instanceof PsiImportStaticStatement || PsiTreeUtil.isAncestor(containingClass, ref, true))) {
return true;
}
if (qualifierExpression instanceof PsiReferenceExpression) {
PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
if (resolve == containingClass) {
return true;
}
if (resolve instanceof PsiTypeParameter) {
Set<PsiClass> classes = new HashSet<>();
for (PsiClassType type : ((PsiTypeParameter)resolve).getExtendsListTypes()) {
final PsiClass aClass = type.resolve();
if (aClass != null) {
classes.add(aClass);
}
}
if (classes.size() == 1 && classes.contains(containingClass)) {
return true;
}
}
}
return false;
}
return true;
}
public static boolean seemsScrambled(@Nullable PsiClass aClass) {
return aClass instanceof PsiCompiledElement && seemsScrambledByStructure(aClass);
}

View File

@@ -238,7 +238,7 @@ valid.switch.selector.types=byte, char, short or int
valid.switch.17.selector.types=char, byte, short, int, Character, Byte, Short, Integer, String, or an enum
dot.expected.after.super.or.this='.' expected
unqualified.super.disallowed=Unqualified super reference is not allowed in extension method
static.interface.method.call.qualifier=Static method may be invoked on containing interface class only
non.static.symbol.referenced.from.static.context=Non-static {0} ''{1}'' cannot be referenced from a static context
private.symbol=''{0}'' has private access in ''{1}''
protected.symbol=''{0}'' has protected access in ''{1}''
@@ -435,6 +435,8 @@ feature.method.references=Method references
feature.lambda.expressions=Lambda expressions
feature.type.annotations=Type annotations
feature.type.receivers=Receiver parameters
feature.intersections.in.casts=Intersection types in casts
feature.static.interface.calls=Static interface method calls
feature.try.with.resources.refs=Resource references
feature.modules=Modules
insufficient.language.level={0} are not supported at this language level
insufficient.language.level={0} are not supported at language level ''{1}''

View File

@@ -20,9 +20,9 @@ public class NumericLiterals {
int i15 = <error descr="Integer number too large">0xf044332211</error>;
int oi1 = 017777777777;
int oi2 = <error descr="Integer number too large">08</error>;
int bi1 = <error descr="Binary literals are not supported at this language level">0b0010</error>;
int bi2 = <error descr="Binary literals are not supported at this language level">0B0010</error>;
int bi3 = <error descr="Underscores in literals are not supported at this language level">1_2</error>;
int bi1 = <error descr="Binary literals are not supported at language level '1.4'">0b0010</error>;
int bi2 = <error descr="Binary literals are not supported at language level '1.4'">0B0010</error>;
int bi3 = <error descr="Underscores in literals are not supported at language level '1.4'">1_2</error>;
long l1 = -<error descr="Long number too large">9223372036854775809L</error>;
long l2 = <error descr="Long number too large">9223372036854775808L</error>;
@@ -36,21 +36,21 @@ public class NumericLiterals {
long l10 = 0x8000000000000000L;
long ol1 = 01777777777777777777600L;
long ol2 = 01777777777777777777777L;
long bl1 = <error descr="Binary literals are not supported at this language level">0b0010l</error>;
long bl2 = <error descr="Binary literals are not supported at this language level">0B0010L</error>;
long bl3 = <error descr="Underscores in literals are not supported at this language level">10_24L</error>;
long bl1 = <error descr="Binary literals are not supported at language level '1.4'">0b0010l</error>;
long bl2 = <error descr="Binary literals are not supported at language level '1.4'">0B0010L</error>;
long bl3 = <error descr="Underscores in literals are not supported at language level '1.4'">10_24L</error>;
float f1= <error descr="Floating point number too small">1e-46f</error>;
float f2 = <error descr="Floating point number too large">1e39f</error>;
float f3 = 0E1F;
float f4 = <error descr="Hexadecimal floating point literals are not supported at this language level">0xabc.defP2f</error>;
float f5 = <error descr="Underscores in literals are not supported at this language level">3.141_592f</error>;
float f4 = <error descr="Hexadecimal floating point literals are not supported at language level '1.4'">0xabc.defP2f</error>;
float f5 = <error descr="Underscores in literals are not supported at language level '1.4'">3.141_592f</error>;
float f6 = .0f;
double dd1 = <error descr="Floating point number too small">1e-324</error>;
double dd2 = <error descr="Floating point number too large">1e309</error>;
double dd3 = 0E1;
double dd4 = <error descr="Hexadecimal floating point literals are not supported at this language level">0x1.fffffffffffffP1023</error>;
double dd4 = <error descr="Hexadecimal floating point literals are not supported at language level '1.4'">0x1.fffffffffffffP1023</error>;
double d1 = <error descr="Malformed floating point literal">1.E</error>;
double d2 = <error descr="Malformed floating point literal">1.e</error>;
double d3 = <error descr="Malformed floating point literal">1.E+</error>;
@@ -62,7 +62,7 @@ public class NumericLiterals {
double d9 = <error descr="Malformed floating point literal">1e-d</error>;
double d10 = <error descr="Malformed floating point literal">1e-F</error>;
double d11 = <error descr="Malformed floating point literal">1e-f</error>;
double d12 = <error descr="Underscores in literals are not supported at this language level">3.141_592_653_589_793d</error>;
double d12 = <error descr="Underscores in literals are not supported at language level '1.4'">3.141_592_653_589_793d</error>;
double d13 = <error descr="Malformed floating point literal">.0e</error>;
}
}
}

View File

@@ -1,19 +1,19 @@
import java.io.*;
import java.util.*;
<error descr="Static imports are not supported at this language level">import static java.lang.Math.*;</error>
<error descr="Static imports are not supported at language level '1.4'">import static java.lang.Math.*;</error>
@interface <error descr="Annotations are not supported at this language level">Anno</error> { }
@interface <error descr="Annotations are not supported at language level '1.4'">Anno</error> { }
<error descr="Annotations are not supported at this language level">@Anno</error>
<error descr="Annotations are not supported at language level '1.4'">@Anno</error>
class UnsupportedFeatures {
void m(<error descr="Variable arity methods are not supported at this language level">String... args</error>) throws Exception {
<error descr="For-each loops are not supported at this language level">for (String s : args) { System.out.println(s); }</error>
void m(<error descr="Variable arity methods are not supported at language level '1.4'">String... args</error>) throws Exception {
<error descr="For-each loops are not supported at language level '1.4'">for (String s : args) { System.out.println(s); }</error>
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>();
List<error descr="Generics are not supported at language level '1.4'"><String></error> list =
new ArrayList<error descr="Generics are not supported at language level '1.4'"><></error>();
<error descr="For-each loops are not supported at this language level">for (Object s : list) {}</error>
<error descr="For-each loops are not supported at language level '1.4'">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>
@@ -21,4 +21,4 @@ class UnsupportedFeatures {
java.lang.annotation.ElementType t = null;
switch (<error descr="Incompatible types. Found: 'java.lang.annotation.ElementType', required: 'byte, char, short or int'">t</error>) { }
}
}
}

View File

@@ -6,7 +6,7 @@ class UnsupportedFeatures {
for (String s : args) { System.out.println(s); }
List<String> list =
new ArrayList<error descr="Diamond types are not supported at this language level"><></error>();
new ArrayList<error descr="Diamond types are not supported at language level '1.6'"><></error>();
for (String s : list) {}
Arrays.asList("");
@@ -14,22 +14,22 @@ class UnsupportedFeatures {
boolean b1 = Boolean.TRUE;
try { Reader r = new FileReader("/dev/null"); }
catch (<error descr="Multi-catches are not supported at this language level">FileNotFoundException | IOException e</error>) { e.printStackTrace(); }
catch (<error descr="Multi-catches are not supported at language level '1.6'">FileNotFoundException | IOException e</error>) { e.printStackTrace(); }
try <error descr="Try-with-resources are not supported at this language level">(Reader r = new FileReader("/dev/null"))</error> { }
try <error descr="Try-with-resources are not supported at language level '1.6'">(Reader r = new FileReader("/dev/null"))</error> { }
I i1 = <error descr="Method references are not supported at this language level">UnsupportedFeatures::m</error>;
I i2 = <error descr="Lambda expressions are not supported at this language level">() -> { }</error>;
I i1 = <error descr="Method references are not supported at language level '1.6'">UnsupportedFeatures::m</error>;
I i2 = <error descr="Lambda expressions are not supported at language level '1.6'">() -> { }</error>;
switch (<error descr="Incompatible types. Found: 'java.lang.String', required: 'byte, char, short or int'">list.get(0)</error>) {
case "foo": break;
}
}
void f(<error descr="Receiver parameters are not supported at this language level">Object this</error>) { }
void f(<error descr="Receiver parameters are not supported at language level '1.6'">Object this</error>) { }
interface I {
<error descr="Extension methods are not supported at this language level">default void m() { }</error>
<error descr="Extension methods are not supported at this language level">static void m() { }</error>
<error descr="Extension methods are not supported at language level '1.6'">default void m() { }</error>
<error descr="Extension methods are not supported at language level '1.6'">static void m() { }</error>
}
}
}

View File

@@ -1,14 +1,14 @@
class Test {
interface I {
<error descr="Extension methods are not supported at this language level">static void foo() {}</error>
<error descr="Extension methods are not supported at language level '1.7'">static void foo() {}</error>
}
abstract class IImpl implements I {}
abstract static class IImpl implements I {}
interface I2 extends I {}
{
<error descr="Static interface method invocations are not supported at this language level">I.foo();</error>
IImpl.<error descr="Static interface method invocations are not supported at this language level">foo</error>();
I2.<error descr="Static interface method invocations are not supported at this language level">foo</error>();
I.<error descr="Static interface method calls are not supported at language level '1.7'">foo</error>();
IImpl.<error descr="Static interface method calls are not supported at language level '1.7'">foo</error>();
I2.<error descr="Static interface method calls are not supported at language level '1.7'">foo</error>();
}
}

View File

@@ -0,0 +1,7 @@
import java.io.Serializable;
class IntersectionTypeCast {
void m(Runnable r) {
Object o = <error descr="Intersection types in casts are not supported at language level '1.7'">(Runnable & Serializable)r</error>;
}
}

View File

@@ -1,3 +1,3 @@
<error descr="Modules are not supported at this language level">module M {
<error descr="Modules are not supported at language level '1.8'">module M {
requires A;
}</error>

View File

@@ -3,6 +3,6 @@ import java.io.*;
class UnsupportedFeatures {
void m() throws Exception {
Reader r1 = new FileReader("/dev/null");
try (<error descr="Resource references are not supported at this language level">r1</error>; Reader r2 = new FileReader("/dev/null")) { }
try (<error descr="Resource references are not supported at language level '1.8'">r1</error>; Reader r2 = new FileReader("/dev/null")) { }
}
}

View File

@@ -15,7 +15,7 @@ abstract class C {
void notWrong() { }
}
class B extends <error descr="Type annotations are not supported at this language level">@Deprecated</error> Object { }
class B extends <error descr="Type annotations are not supported at language level '1.7'">@Deprecated</error> Object { }
enum E {
@Anno E1
@@ -24,7 +24,7 @@ enum E {
interface I {
@<error descr="Duplicate annotation">Anno</error>
public @<error descr="Duplicate annotation">Anno</error>
Collection<<error descr="Type annotations are not supported at this language level">@Anno</error> String>
Collection<<error descr="Type annotations are not supported at language level '1.7'">@Anno</error> String>
method(@<error descr="Duplicate annotation">Anno</error> @<error descr="Duplicate annotation">Anno</error> Object o);
}
@@ -36,4 +36,4 @@ interface I {
@interface Part { }
Part[] arrayValue() default {@Part, @Part};
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -182,6 +182,7 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
public void testRuntimeClassCast() { doTest(true, false);}
public void testTryWithResourcesWithMultipleCloseInterfaces() { doTest(false, false);}
public void testIDEA138978() { doTest(false, false); }
public void testIntersectionTypeCast() { doTest(false, false); }
public void testArrayInitializerTypeCheckVariableType() { doTest(false, false);}