extension for lombok val support (IDEABKL-5667)

This commit is contained in:
Anna Kozlova
2014-12-19 19:31:02 +01:00
parent 1400e9cfa8
commit 09dc54d415
4 changed files with 154 additions and 0 deletions

View File

@@ -18,7 +18,10 @@ package com.intellij.psi.augment;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,4 +47,28 @@ public abstract class PsiAugmentProvider {
return result;
}
/**
* Extends {@link PsiTypeElement#getType()} so type could be retrieved from external place
* e.g. from variable initializer in lombok case (http://projectlombok.org/features/val.html)
*
* @param typeElement place where inference takes place,
* also nested PsiTypeElement-s (e.g. for List<String> PsiTypeElements corresponding to both List and String would be suggested)
* @return inferred type or null, if inference is not applicable
*/
@Nullable
protected PsiType inferType(PsiTypeElement typeElement) {
return null;
}
@Nullable
public static PsiType getInferredType(PsiTypeElement typeElement) {
for (PsiAugmentProvider provider : Extensions.getExtensions(EP_NAME)) {
final PsiType type = provider.inferType(typeElement);
if (type != null) {
return type;
}
}
return null;
}
}

View File

@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
@@ -76,6 +77,11 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
}
private PsiType calculateType() {
final PsiType inferredType = PsiAugmentProvider.getInferredType(this);
if (inferredType != null) {
return inferredType;
}
PsiType type = null;
SmartList<PsiAnnotation> annotations = new SmartList<PsiAnnotation>();

View File

@@ -0,0 +1,25 @@
import lombok.val;
import java.util.*;
class Test {
<error descr="Incompatible types. Found: 'int', required: 'lombok.val'">val field = 0;</error>
void method(val param) {
<error descr="Incompatible types. Found: 'lombok.val', required: 'int'">int p = param;</error>
val i = 0;
int j = i + 1;
val a = new ArrayList<String>();
Object o = a.get(0);
val b = new ArrayList<>();
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String s = b.get(0);</error>
o = b.get(0);
for (val v : a) {
String vStr = v;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2000-2014 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.codeInsight.daemon;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class PsiAugmentProviderTest extends LightCodeInsightFixtureTestCase {
private static final String LOMBOK_VAL_FQN = "lombok.val";
private static final String LOMBOK_VAL_SHORT_NAME = "val";
public void testLombokVal() {
PlatformTestUtil.registerExtension(PsiAugmentProvider.EP_NAME, new PsiAugmentProvider() {
@NotNull
@Override
public <Psi extends PsiElement> List<Psi> getAugments(@NotNull PsiElement element, @NotNull Class<Psi> type) {
return Collections.<Psi>emptyList();
}
@Nullable
@Override
protected PsiType inferType(PsiTypeElement typeElement) {
final PsiElement parent = typeElement.getParent();
if (parent instanceof PsiLocalVariable && ((PsiLocalVariable)parent).getInitializer() != null ||
parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiForeachStatement) {
final String text = typeElement.getText();
if (LOMBOK_VAL_SHORT_NAME.equals(text) || LOMBOK_VAL_FQN.equals(text)) {
final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
if (referenceElement != null) {
final PsiElement resolve = referenceElement.resolve();
if (resolve instanceof PsiClass) {
if (parent instanceof PsiLocalVariable) {
final PsiExpression initializer = ((PsiVariable)parent).getInitializer();
assertNotNull(initializer);
final PsiType initializerType = initializer.getType();
if (initializer instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement reference = ((PsiNewExpression)initializer).getClassOrAnonymousClassReference();
if (reference != null) {
final PsiReferenceParameterList parameterList = reference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] elements = parameterList.getTypeParameterElements();
if (elements.length == 1 && elements[0].getType() instanceof PsiDiamondType) {
return TypeConversionUtil.erasure(initializerType);
}
}
}
}
return initializerType;
}
final PsiForeachStatement foreachStatement = (PsiForeachStatement)((PsiParameter)parent).getDeclarationScope();
assertNotNull(foreachStatement);
final PsiExpression iteratedValue = foreachStatement.getIteratedValue();
if (iteratedValue != null) {
return JavaGenericsUtil.getCollectionItemType(iteratedValue);
}
}
}
}
}
return null;
}
}, myTestRootDisposable);
myFixture.addClass("package lombok; public @interface val{}");
myFixture.testHighlighting(false, false, false, getTestName(false) + ".java");
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/augment";
}
}