Type annotation in wildcard and class reference types; more tests

This commit is contained in:
Roman Shevchenko
2013-04-05 21:08:12 +02:00
parent 69242b8a27
commit 218fd54bf2
9 changed files with 222 additions and 91 deletions
@@ -16,12 +16,15 @@
package com.intellij.psi;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
import static com.intellij.util.ObjectUtils.notNull;
/**
* Service for validating and parsing Java identifiers.
*
@@ -103,13 +106,54 @@ public abstract class PsiNameHelper {
return sub.length() == referenceText.length() ? sub : new String(sub);
}
public static String getPresentableText(PsiJavaCodeReferenceElement ref) {
final String referenceName = ref.getReferenceName();
PsiType[] typeParameters = ref.getTypeParameters();
return getPresentableText(referenceName, typeParameters);
@NotNull
public static String getPresentableText(@NotNull PsiJavaCodeReferenceElement ref) {
String name = ref.getReferenceName();
PsiAnnotation[] children = PsiTreeUtil.getChildrenOfType(ref, PsiAnnotation.class);
PsiAnnotation[] annotations = notNull(children, PsiAnnotation.EMPTY_ARRAY);
PsiReferenceParameterList parameterList = ref.getParameterList();
PsiTypeElement[] typeElements = parameterList != null ? parameterList.getTypeParameterElements() : PsiTypeElement.EMPTY_ARRAY;
return getPresentableText(name, annotations, typeElements);
}
public static String getPresentableText(final String referenceName, final PsiType[] typeParameters) {
@NotNull
public static String getPresentableText(@Nullable String referenceName,
@NotNull PsiAnnotation[] annotations,
@NotNull PsiTypeElement[] typeElements) {
if (typeElements.length == 0 && annotations.length == 0) {
return referenceName != null ? referenceName : "";
}
StringBuilder buffer = new StringBuilder();
if (annotations.length > 0) {
for (PsiAnnotation annotation : annotations) {
buffer.append(annotation.getText()).append(' ');
}
}
buffer.append(referenceName);
if (typeElements.length > 0) {
buffer.append("<");
for (int i = 0; i < typeElements.length; i++) {
PsiType type = typeElements[i].getType();
if (!(type instanceof PsiDiamondType)) {
buffer.append(type.getPresentableText());
if (i < typeElements.length - 1) buffer.append(", ");
}
}
buffer.append(">");
}
return buffer.toString();
}
/** deprecated use {@link #getPresentableText(String, PsiAnnotation[], PsiTypeElement[])} (to remove in IDEA 13) */
public static String getPresentableText(@Nullable String referenceName, @NotNull PsiType[] typeParameters) {
if (typeParameters.length > 0) {
StringBuilder buffer = new StringBuilder();
buffer.append(referenceName);
@@ -244,7 +244,12 @@ public abstract class PsiType implements PsiAnnotationOwner {
@Override
public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
return null; //todo
for (PsiAnnotation annotation : myAnnotations) {
if (qualifiedName.equals(annotation.getQualifiedName())) {
return annotation;
}
}
return null;
}
@Override
@@ -96,38 +96,19 @@ public class PsiWildcardType extends PsiType {
@Override
public String getPresentableText() {
if (myBound == null) return "?";
if (myIsExtending) {
return EXTENDS_PREFIX + myBound.getPresentableText();
}
else {
//noinspection HardCodedStringLiteral
return "? super " + myBound.getPresentableText();
}
return getAnnotationsTextPrefix(false, false, true) +
(myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getPresentableText());
}
@Override
public String getCanonicalText() {
if (myBound == null) return "?";
if (myIsExtending) {
return EXTENDS_PREFIX + myBound.getCanonicalText();
}
else {
//noinspection HardCodedStringLiteral
return "? super " + myBound.getCanonicalText();
}
return (myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getCanonicalText());
}
@Override
public String getInternalCanonicalText() {
if (myBound == null) return "?";
if (myIsExtending) {
return EXTENDS_PREFIX + myBound.getInternalCanonicalText();
}
else {
//noinspection HardCodedStringLiteral
return "? super " + myBound.getInternalCanonicalText();
}
return getAnnotationsTextPrefix(true, false, true) +
(myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getInternalCanonicalText());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -17,7 +17,6 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
@@ -38,43 +37,19 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements PsiJavaCodeReferenceElement {
private static final ClsTypeElementImpl[] EMPTY_ARRAY = new ClsTypeElementImpl[0];
@NonNls private static final String EXTENDS_PREFIX = "?extends";
@NonNls private static final String SUPER_PREFIX = "?super";
private final PsiElement myParent;
private final String myCanonicalText;
private final String myQualifiedName;
private final ClsTypeElementImpl[] myTypeParameters; // in right-to-left order
private volatile PsiType[] myTypeParametersCachedTypes = null; // in left-to-right-order
private final PsiReferenceParameterList myRefParameterList;
public ClsJavaCodeReferenceElementImpl(PsiElement parent, String canonicalText) {
myParent = parent;
myCanonicalText = canonicalText;
final String[] classParametersText = PsiNameHelper.getClassParametersText(canonicalText);
int length = classParametersText.length;
myTypeParameters = length == 0 ? EMPTY_ARRAY : new ClsTypeElementImpl[length];
for (int i = 0; i < length; i++) {
String s = classParametersText[length - i - 1];
char variance = ClsTypeElementImpl.VARIANCE_NONE;
if (s.startsWith(EXTENDS_PREFIX)) {
variance = ClsTypeElementImpl.VARIANCE_EXTENDS;
s = s.substring(EXTENDS_PREFIX.length());
}
else if (s.startsWith(SUPER_PREFIX)) {
variance = ClsTypeElementImpl.VARIANCE_SUPER;
s = s.substring(SUPER_PREFIX.length());
}
else if (StringUtil.startsWithChar(s, '?')) {
variance = ClsTypeElementImpl.VARIANCE_INVARIANT;
s = s.substring(1);
}
myTypeParameters[i] = new ClsTypeElementImpl(this, s, variance);
}
myQualifiedName = PsiNameHelper.getQualifiedClassName(myCanonicalText, false);
String[] classParameters = PsiNameHelper.getClassParametersText(canonicalText);
myRefParameterList = classParameters.length == 0 ? null : new ClsReferenceParameterListImpl(this, classParameters);
}
@Override
@@ -121,19 +96,23 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
}
private JavaResolveResult advancedResolveImpl() {
final PsiElement resolve = resolveElement();
PsiTypeElement[] typeElements = myRefParameterList == null ? PsiTypeElement.EMPTY_ARRAY : myRefParameterList.getTypeParameterElements();
PsiElement resolve = resolveElement();
if (resolve instanceof PsiClass) {
final Map<PsiTypeParameter, PsiType> substitutionMap = new HashMap<PsiTypeParameter, PsiType>();
Map<PsiTypeParameter, PsiType> substitutionMap = new HashMap<PsiTypeParameter, PsiType>();
int index = 0;
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable((PsiClass)resolve)) {
if (index >= myTypeParameters.length) {
final PsiTypeParameterListOwner parameterOwner = parameter.getOwner();
if (index >= typeElements.length) {
PsiTypeParameterListOwner parameterOwner = parameter.getOwner();
if (parameterOwner == resolve) {
substitutionMap.put(parameter, null);
} else if (parameterOwner instanceof PsiClass) {
}
else if (parameterOwner instanceof PsiClass) {
PsiElement containingClass = myParent;
while ((containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class, true)) != null) {
final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getClassSubstitutor((PsiClass)parameterOwner, (PsiClass)containingClass, PsiSubstitutor.EMPTY);
PsiSubstitutor superClassSubstitutor =
TypeConversionUtil.getClassSubstitutor((PsiClass)parameterOwner, (PsiClass)containingClass, PsiSubstitutor.EMPTY);
if (superClassSubstitutor != null) {
substitutionMap.put(parameter, superClassSubstitutor.substitute(parameter));
break;
@@ -142,7 +121,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
}
}
else {
substitutionMap.put(parameter, myTypeParameters[index].getType());
substitutionMap.put(parameter, typeElements[index].getType());
}
index++;
}
@@ -228,7 +207,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
@Override
public PsiReferenceParameterList getParameterList() {
return null;
return myRefParameterList;
}
@Override
@@ -307,15 +286,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
@Override
@NotNull
public PsiType[] getTypeParameters() {
PsiType[] cachedTypes = myTypeParametersCachedTypes;
if (cachedTypes == null) {
cachedTypes = myTypeParameters.length == 0 ? PsiType.EMPTY_ARRAY : new PsiType[myTypeParameters.length];
for (int i = 0; i < cachedTypes.length; i++) {
cachedTypes[cachedTypes.length - i - 1] = myTypeParameters[i].getType();
}
myTypeParametersCachedTypes = cachedTypes;
}
return cachedTypes;
return myRefParameterList == null ? PsiType.EMPTY_ARRAY : myRefParameterList.getTypeArguments();
}
@Override
@@ -0,0 +1,94 @@
/*
* Copyright 2000-2013 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.psi.impl.compiled;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.TreeElement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class ClsReferenceParameterListImpl extends ClsElementImpl implements PsiReferenceParameterList {
@NonNls private static final String EXTENDS_PREFIX = "?extends";
@NonNls private static final String SUPER_PREFIX = "?super";
private final PsiElement myParent;
private final ClsTypeElementImpl[] myTypeParameters;
private volatile PsiType[] myTypeParametersCachedTypes = null;
public ClsReferenceParameterListImpl(PsiElement parent, String[] classParameters) {
myParent = parent;
int length = classParameters.length;
myTypeParameters = new ClsTypeElementImpl[length];
for (int i = 0; i < length; i++) {
String s = classParameters[length - i - 1];
char variance = ClsTypeElementImpl.VARIANCE_NONE;
if (s.startsWith(EXTENDS_PREFIX)) {
variance = ClsTypeElementImpl.VARIANCE_EXTENDS;
s = s.substring(EXTENDS_PREFIX.length());
}
else if (s.startsWith(SUPER_PREFIX)) {
variance = ClsTypeElementImpl.VARIANCE_SUPER;
s = s.substring(SUPER_PREFIX.length());
}
else if (StringUtil.startsWithChar(s, '?')) {
variance = ClsTypeElementImpl.VARIANCE_INVARIANT;
s = s.substring(1);
}
myTypeParameters[i] = new ClsTypeElementImpl(this, s, variance);
}
}
@Override
public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) { }
@Override
public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException { }
@NotNull
@Override
public PsiTypeElement[] getTypeParameterElements() {
return myTypeParameters;
}
@NotNull
@Override
public PsiType[] getTypeArguments() {
PsiType[] cachedTypes = myTypeParametersCachedTypes;
if (cachedTypes == null) {
cachedTypes = myTypeParameters.length == 0 ? PsiType.EMPTY_ARRAY : new PsiType[myTypeParameters.length];
for (int i = 0; i < cachedTypes.length; i++) {
cachedTypes[cachedTypes.length - i - 1] = myTypeParameters[i].getType();
}
myTypeParametersCachedTypes = cachedTypes;
}
return cachedTypes;
}
@NotNull
@Override
public PsiElement[] getChildren() {
return myTypeParameters;
}
@Override
public PsiElement getParent() {
return myParent;
}
}
@@ -0,0 +1,12 @@
// "Cast to 'int'" "true"
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE_USE}) @interface TA { String value() default ""; }
class C {
{
Object o = null;
@TA("wtf") <caret>int i = (@TA("wtf") int) o;
}
}
@@ -8,6 +8,6 @@ import static java.lang.annotation.ElementType.*;
class C {
{
Object o = null;
@TA <caret>List<@TA ? extends @TA String> l = (@TA List<? extends @TA String>) o;
@TA List<@TA ? extends @TA String> l = (@TA List<@TA ? extends @TA String>) o;
}
}
@@ -0,0 +1,12 @@
// "Cast to 'int'" "true"
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE_USE}) @interface TA { String value() default ""; }
class C {
{
Object o = null;
@TA("wtf") <caret>int i = o;
}
}
@@ -15,34 +15,46 @@
*/
package com.intellij.codeInsight.psi
import com.intellij.psi.*
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.testFramework.LightIdeaTestCase
@SuppressWarnings(["GrUnresolvedAccess"])
class AnnotatedTypeTest extends LightIdeaTestCase {
@SuppressWarnings("GrUnresolvedAccess")
public void testTypeComposition() {
def context = createFile("typeCompositionTest.java", """
PsiFile context = createFile("typeCompositionTest.java", """
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@interface A { }
@Target({TYPE_USE}) @interface TA { }
@Target({TYPE_USE}) @interface TA { int value() default 42; }
class E1 extends Exception { }
class E2 extends Exception { }
""")
def factory = JavaPsiFacade.getInstance(getProject()).getElementFactory(), psi
PsiElement psi
psi = factory.createStatementFromText("@TA int @TA [] a = null", context)
assertEquals("@TA int @TA []", psi.declaredElements[0].type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("@A @TA(1) int @TA(2) [] a", context)
assertEquals("@TA(1) int @TA(2) []", psi.declaredElements[0].type.presentableText)
psi = factory.createStatementFromText("@A int @TA [] a = null", context)
assertEquals("int @TA []", psi.declaredElements[0].type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("try { } catch (@A @TA(1) E1 | @TA(2) E2 e) { }", context)
assertEquals("@TA(1) E1 | @TA(2) E2", psi.catchBlockParameters[0].type.presentableText)
psi = factory.createStatementFromText("try { } catch (@TA E1 | @TA E2 e) { }", context)
assertEquals("@TA E1 | @TA E2", psi.catchBlockParameters[0].type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("@A @TA(1) String @TA(2) [] f @TA(3) []", context)
assertEquals("@TA(1) String @TA(2) [] @TA(3) []", psi.declaredElements[0].type.presentableText)
psi = factory.createFieldFromText("@TA String @TA [] f @TA []", context)
assertEquals("@TA String @TA [] @TA []", psi.type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("Class<@TA(1) ?> c", context)
assertEquals("Class<@TA(1) ?>", psi.declaredElements[0].type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("Class cs = new Class<>()", context)
assertEquals("Class<>", psi.declaredElements[0].initializer.type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("@A @TA(1) String s", context)
assertEquals("@TA(1) String", psi.declaredElements[0].type.presentableText)
psi = javaFacade.elementFactory.createStatementFromText("@A java.lang.@TA(1) String s", context)
assertEquals("@TA(1) String", psi.declaredElements[0].type.presentableText)
}
}