mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
initial highlighting for LVTI (IDEA-178868)
This commit is contained in:
+40
-1
@@ -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;
|
||||
|
||||
+1
@@ -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();
|
||||
|
||||
+58
@@ -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) { }
|
||||
|
||||
}
|
||||
}
|
||||
+46
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user