diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 5e10a8ee9915..fd9e781fd4ee 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -546,6 +546,43 @@ public class HighlightUtil extends HighlightUtilBase { return highlightInfo; } + static HighlightInfo checkVarTypeApplicability(@NotNull PsiVariable variable) { + PsiTypeElement typeElement = variable.getTypeElement(); + if (typeElement != null && typeElement.isInferredType()) { + + + PsiElement parent = variable.getParent(); + if (variable instanceof PsiLocalVariable) { + PsiType lType = variable.getType(); + PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .descriptionAndTooltip("Cannot infer type: 'var' on variable without initializer") + .range(variable).create(); + } + PsiLocalVariable[] localVariables = PsiTreeUtil.getChildrenOfType(parent, PsiLocalVariable.class); + if (localVariables.length > 1) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .descriptionAndTooltip("'var' is not allowed in a compound declaration") + .range(variable).create(); + } + + if (lType instanceof PsiArrayType) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .descriptionAndTooltip("'var' is not allowed as an element type of an array") + .range(variable) + .create(); + } + + if (PsiType.NULL.equals(lType)) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Cannot infer type: variable initializer is 'null'") + .range(variable).create(); + } + } + } + return null; + } + @Nullable static HighlightInfo checkAssignability(@Nullable PsiType lType, @Nullable PsiType rType, @@ -1942,7 +1979,9 @@ public class HighlightUtil extends HighlightUtilBase { PsiElement parent = expression.getParent(); if (parent instanceof PsiVariable) { PsiVariable variable = (PsiVariable)parent; - if (variable.getType() instanceof PsiArrayType) return null; + PsiTypeElement typeElement = variable.getTypeElement(); + boolean disabledForInferredType = typeElement == null || !typeElement.isInferredType(); + if (disabledForInferredType && variable.getType() instanceof PsiArrayType) return null; } else if (parent instanceof PsiNewExpression || parent instanceof PsiArrayInitializerExpression) { return null; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 67d07f5f5f27..340bb8c582ac 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -1643,6 +1643,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh public void visitVariable(PsiVariable variable) { super.visitVariable(variable); try { + if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkVarTypeApplicability(variable)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkVariableInitializerType(variable)); } catch (IndexNotReadyException ignored) { } diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index dd5c8322cb1e..2b541d15b464 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -467,7 +467,7 @@ public class ExpectedTypesProvider { PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); PsiClass iterableClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Iterable", statement.getResolveScope()); - if (iterableClass != null && iterableClass.getTypeParameters().length == 1) { + if (iterableClass != null && iterableClass.getTypeParameters().length == 1 && !PsiType.NULL.equals(type)) { Map map = new HashMap<>(); map.put(iterableClass.getTypeParameters()[0], PsiWildcardType.createExtends(manager, type)); PsiType iterableType = factory.createType(iterableClass, factory.createSubstitutor(map)); diff --git a/java/java-psi-api/src/com/intellij/psi/PsiTypeElement.java b/java/java-psi-api/src/com/intellij/psi/PsiTypeElement.java index 3b881ffef515..034f132e406b 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiTypeElement.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiTypeElement.java @@ -55,4 +55,13 @@ public interface PsiTypeElement extends PsiElement, PsiAnnotationOwner { */ @Nullable PsiJavaCodeReferenceElement getInnermostComponentReferenceElement(); + + + /** + * Returns true when local variable is declared as {code}var local = 1;{code} + * @return + */ + default boolean isInferredType() { + return false; + } } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java index ea3efba7ed12..bcebf640a219 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java @@ -247,6 +247,10 @@ public class PsiTypesUtil { final PsiElement parent = PsiUtil.skipParenthesizedExprUp(element.getParent()); if (parent instanceof PsiVariable) { if (PsiUtil.checkSameExpression(element, ((PsiVariable)parent).getInitializer())) { + PsiTypeElement typeElement = ((PsiVariable)parent).getTypeElement(); + if (typeElement != null && typeElement.isInferredType()) { + return null; + } return ((PsiVariable)parent).getType(); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java index 5f7f2c9c80fe..62153c8e853e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl.source; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Computable; import com.intellij.psi.*; @@ -73,6 +74,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl PsiType type = null; List annotations = new SmartList<>(); + PsiElement parent = getParent(); for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof PsiComment || child instanceof PsiWhiteSpace) continue; @@ -96,10 +98,24 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl } else if (PsiUtil.isJavaToken(child, JavaTokenType.VAR_KEYWORD)) { assert type == null : this; - for (PsiElement e = this; e != null; e = e.getNextSibling()) { - if (e instanceof PsiExpression) { - type = ((PsiExpression)e).getType(); - break; + if (parent instanceof PsiParameter) { + PsiElement declarationScope = ((PsiParameter)parent).getDeclarationScope(); + if (declarationScope instanceof PsiForeachStatement) { + PsiExpression iteratedValue = ((PsiForeachStatement)declarationScope).getIteratedValue(); + if (iteratedValue != null) { + type = JavaGenericsUtil.getCollectionItemType(iteratedValue); + } + } + } + else { + for (PsiElement e = this; e != null; e = e.getNextSibling()) { + if (e instanceof PsiExpression) { + if (!(e instanceof PsiArrayInitializerExpression) && + !isSelfReferenced((PsiExpression)e, parent)) { + type = ((PsiExpression)e).getType(); + } + break; + } } } } @@ -151,7 +167,6 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl if (type == null) return PsiType.NULL; - PsiElement parent = getParent(); if (parent instanceof PsiModifierListOwner) { type = JavaSharedImplUtil.applyAnnotations(type, ((PsiModifierListOwner)parent).getModifierList()); } @@ -159,6 +174,38 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl return type; } + private static boolean isSelfReferenced(PsiExpression initializer, PsiElement parent) { + class SelfReferenceVisitor extends JavaRecursiveElementVisitor { + private boolean referenced = false; + + @Override + public void visitElement(PsiElement element) { + if (referenced) return; + super.visitElement(element); + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (expression.resolve() == parent) { + referenced = true; + } + } + } + + SelfReferenceVisitor visitor = new SelfReferenceVisitor(); + initializer.accept(visitor); + return visitor.referenced; + } + + @Override + public boolean isInferredType() { + for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { + if (PsiUtil.isJavaToken(child, JavaTokenType.VAR_KEYWORD)) return true; + } + return false; + } + @NotNull private Computable getReferenceComputable(PsiJavaCodeReferenceElement ref) { final PsiElement parent = getParent(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advLVTI/SimpleAvailability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advLVTI/SimpleAvailability.java new file mode 100644 index 000000000000..2bf4984053c7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advLVTI/SimpleAvailability.java @@ -0,0 +1,58 @@ + +class Main { + private static void localVariableDeclaration() { + var a = 1; + var b = 2, c = 3.0; + var d[] = new int[4]; + var e; + var f = { 6 }; + var g = (g = 7); + } + + private static void localVariableType() { + var a = 1; + int al = a; + + var b = java.util.Arrays.asList(1, 2); + Integer bl = b.get(0); + + var c = "x".getClass(); + Class cl = c; + + var d = new Object() {}; + + var e = (CharSequence & Comparable) "x"; + int el = e.compareTo(""); + + var f = () -> "hello"; + var m = Main::localVariableDeclaration; + var g = null; + } + + private void forEachType(String[] strs, Iterable it, Iterable raw) { + for (var str : strs) { + String s = str; + } + + for (var str : it) { + String s = str; + str = s; + } + + for (var o : raw) { + Object obj = o; + } + + for (var v: ) {} + + for (var v: null) {} + + for (var v : (v)) {} + } + + private void tryWithResources(AutoCloseable c) throws Exception { + try (var v = null) { } + try (var v = c) { } + + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvLVTIHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvLVTIHighlightingTest.java new file mode 100644 index 000000000000..f60827331381 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvLVTIHighlightingTest.java @@ -0,0 +1,46 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.java.codeInsight.daemon; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; + +public class LightAdvLVTIHighlightingTest extends LightDaemonAnalyzerTestCase { + private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/advLVTI"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + setLanguageLevel(LanguageLevel.JDK_X); + IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_9, getModule(), getTestRootDisposable()); + } + + private void doTest() { + doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); + } + + public void testSimpleAvailability() { + doTest(); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk9(); + } +}