mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Type annotation in wildcard and class reference types; more tests
This commit is contained in:
@@ -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
|
||||
|
||||
+18
-47
@@ -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
|
||||
|
||||
+94
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user