initial highlighting for LVTI (IDEA-178868)

This commit is contained in:
Anna Kozlova
2017-09-18 17:04:30 +03:00
parent e91491a9dd
commit 2180c05b71
8 changed files with 211 additions and 7 deletions
@@ -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;
@@ -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) { }
@@ -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<PsiTypeParameter, PsiType> map = new HashMap<>();
map.put(iterableClass.getTypeParameters()[0], PsiWildcardType.createExtends(manager, type));
PsiType iterableType = factory.createType(iterableClass, factory.createSubstitutor(map));
@@ -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;
}
}
@@ -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();
}
}
@@ -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<PsiAnnotation> 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<PsiJavaCodeReferenceElement> getReferenceComputable(PsiJavaCodeReferenceElement ref) {
final PsiElement parent = getParent();
@@ -0,0 +1,58 @@
class Main {
private static void localVariableDeclaration() {
var a = 1;
<error descr="'var' is not allowed in a compound declaration">var b = 2</error>, <error descr="'var' is not allowed in a compound declaration">c = 3.0;</error>
<error descr="'var' is not allowed as an element type of an array">var d[] = new int[4];</error>
<error descr="Cannot infer type: 'var' on variable without initializer">var e;</error>
var f = <error descr="Array initializer is not allowed here">{ 6 }</error>;
var g = (<error descr="Incompatible types. Found: 'int', required: 'null'">g = 7</error>);
}
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<? extends String> cl = c;
var d = new Object() {};
var e = (CharSequence & Comparable<String>) "x";
int el = e.compareTo("");
var f = <error descr="<lambda expression> is not a functional interface">() -> "hello"</error>;
var m = <error descr="<method reference> is not a functional interface">Main::localVariableDeclaration</error>;
<error descr="Cannot infer type: variable initializer is 'null'">var g = null;</error>
}
private void forEachType(String[] strs, Iterable<String> 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:<error descr="Expression expected"> </error>) {}
for (var v: <error descr="foreach not applicable to type 'null'">null</error>) {}
for (var v : (<error descr="Cannot resolve symbol 'v'">v</error>)) {}
}
private void tryWithResources(AutoCloseable c) throws Exception {
try (<error descr="Cannot infer type: variable initializer is 'null'">var v = null</error>) { }
try (var v = c) { }
}
}
@@ -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();
}
}