Type annotations on C-style arrays are recognized now

This commit is contained in:
Roman Shevchenko
2013-03-31 23:13:14 +02:00
parent 4b76c9d474
commit 8bb33ccdca
7 changed files with 139 additions and 30 deletions
@@ -405,7 +405,7 @@ public class DeclarationParser {
private PsiBuilder.Marker parseMethodFromLeftParenth(PsiBuilder builder, PsiBuilder.Marker declaration, boolean anno, boolean constructor) {
parseParameterList(builder);
eatBrackets(builder, constructor, "expected.semicolon");
eatBrackets(builder, constructor ? "expected.semicolon" : null);
myParser.getReferenceParser().parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA);
@@ -600,7 +600,7 @@ public class DeclarationParser {
if (expect(builder, JavaTokenType.IDENTIFIER)) {
if (!resource) {
eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, "expected.rparen");
eatBrackets(builder, typeInfo != null && typeInfo.isVarArg ? "expected.rparen" : null);
done(param, JavaElementType.PARAMETER);
return param;
}
@@ -644,7 +644,7 @@ public class DeclarationParser {
while (true) {
shouldRollback = true;
if (!eatBrackets(builder, false, null)) {
if (!eatBrackets(builder, null)) {
unclosed = true;
}
@@ -706,26 +706,33 @@ public class DeclarationParser {
return declaration;
}
private static boolean eatBrackets(final PsiBuilder builder, final boolean isError,
@Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) {
if (builder.getTokenType() != JavaTokenType.LBRACKET) return true;
private boolean eatBrackets(PsiBuilder builder, @Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) {
IElementType tokenType = builder.getTokenType();
if (tokenType != JavaTokenType.LBRACKET && tokenType != JavaTokenType.AT) return true;
final PsiBuilder.Marker marker = isError ? builder.mark() : null;
PsiBuilder.Marker marker = errorKey != null ? builder.mark() : null;
boolean result = true;
while (expect(builder, JavaTokenType.LBRACKET)) {
if (!expect(builder, JavaTokenType.RBRACKET)) {
if (!isError) error(builder, JavaErrorMessages.message("expected.rbracket"));
result = false;
int count = 0;
while (true) {
parseAnnotations(builder);
if (!expect(builder, JavaTokenType.LBRACKET)) {
break;
}
++count;
if (!expect(builder, JavaTokenType.RBRACKET)) {
break;
}
++count;
}
if (marker != null && errorKey != null) {
boolean paired = count % 2 == 0;
if (marker != null) {
marker.error(JavaErrorMessages.message(errorKey));
}
return result;
else if (!paired) {
error(builder, JavaErrorMessages.message("expected.rbracket"));
}
return paired;
}
@Nullable
@@ -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.
@@ -27,7 +27,12 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.CharTable;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class JavaSharedImplUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaSharedImplUtil");
@@ -35,7 +40,6 @@ public class JavaSharedImplUtil {
private JavaSharedImplUtil() { }
public static PsiType getType(@NotNull PsiTypeElement typeElement, @NotNull PsiElement anchor, @NotNull PsiElement context) {
int cStyleArrayCount = countBrackets(anchor);
PsiType type;
if (typeElement instanceof PsiTypeElementImpl) {
type = ((PsiTypeElementImpl)typeElement).getDetachedType(context);
@@ -43,34 +47,61 @@ public class JavaSharedImplUtil {
else {
type = typeElement.getType();
}
for (int i = 0; i < cStyleArrayCount; i++) {
type = type.createArrayType();
List<PsiAnnotation[]> allAnnotations = collectAnnotations(anchor);
for (PsiAnnotation[] annotations : allAnnotations) {
type = type.createArrayType(annotations);
}
return type;
}
public static PsiType getTypeNoResolve(@NotNull PsiTypeElement typeElement, @NotNull PsiElement anchor, @NotNull PsiElement context) {
int cStyleArrayCount = countBrackets(anchor);
PsiType type = typeElement.getTypeNoResolve(context);
for (int i = 0; i < cStyleArrayCount; i++) {
type = type.createArrayType();
List<PsiAnnotation[]> allAnnotations = collectAnnotations(anchor);
for (PsiAnnotation[] annotations : allAnnotations) {
type = type.createArrayType(annotations);
}
return type;
}
private static int countBrackets(PsiElement anchor) {
int cStyleArrayCount = 0;
ASTNode name = SourceTreeToPsiMap.psiToTreeNotNull(anchor);
for (ASTNode child = name.getTreeNext(); child != null; child = child.getTreeNext()) {
IElementType i = child.getElementType();
private static List<PsiAnnotation[]> collectAnnotations(PsiElement anchor) {
List<PsiAnnotation[]> annotations = new SmartList<PsiAnnotation[]>();
List<PsiAnnotation> current = null;
for (PsiElement child = anchor.getNextSibling(); child != null; child = child.getNextSibling()) {
if (child instanceof PsiAnnotation) {
if (current == null) current = new SmartList<PsiAnnotation>();
current.add((PsiAnnotation)child);
continue;
}
IElementType i = child.getNode().getElementType();
if (i == JavaTokenType.LBRACKET) {
cStyleArrayCount++;
annotations.add(ContainerUtil.toArray(current, PsiAnnotation.ARRAY_FACTORY));
current = null;
}
else if (i != JavaTokenType.RBRACKET && !ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(i)) {
break;
}
}
return cStyleArrayCount;
return annotations;
}
@Nullable
public static PsiType findAnnotatedSubtype(@Nullable PsiType type, @NotNull PsiAnnotation annotation) {
while (type instanceof PsiArrayType) {
for (PsiAnnotation a : type.getAnnotations()) {
if (a == annotation) {
return type;
}
}
type = ((PsiArrayType)type).getComponentType();
}
return null;
}
public static void normalizeBrackets(PsiVariable variable) {
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.java.stubs.PsiAnnotationStub;
import com.intellij.psi.impl.meta.MetaRegistry;
import com.intellij.psi.impl.source.JavaStubPsiElement;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.tree.JavaSharedImplUtil;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PairFunction;
@@ -123,6 +124,11 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
return ((PsiNewExpression)parent).getOwner(this);
}
if (parent instanceof PsiMethod) {
PsiType type = ((PsiMethod)parent).getReturnType();
return JavaSharedImplUtil.findAnnotatedSubtype(type, this);
}
if (parent instanceof PsiReferenceExpression) {
PsiElement ctx = parent.getParent();
if (ctx instanceof PsiMethodReferenceExpression) {
@@ -108,6 +108,8 @@ class Outer {
new Boolean @<error descr="Duplicate annotation">TA</error> @<error descr="Duplicate annotation">TA</error> [42];
}
int @TA [] mixedArrays() @TA [] <error descr="Annotations are not allowed here">@TA</error> { return new int[0][0]; }
@TA Outer() { }
class MyClass<@TA @TPA T> { }
@@ -65,6 +65,8 @@ class SpecSamples {
@NonNegative int @NonEmpty [] ints = new @NonNegative int @MinSize(2) [2];
}
int m() @Slowpoke [] @Slowbro [] { return null; }
//
// 4. A type annotation is permitted in front of a constructor declaration ...
//
@@ -1404,6 +1404,57 @@ PsiJavaFile:TypeAnnotations.java
PsiWhiteSpace('\n ')
PsiJavaToken:RBRACE('}')
PsiWhiteSpace('\n\n ')
PsiMethod:m
PsiModifierList:
<empty list>
PsiTypeParameterList
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:m('m')
PsiParameterList:()
PsiJavaToken:LPARENTH('(')
PsiJavaToken:RPARENTH(')')
PsiWhiteSpace(' ')
PsiAnnotation
PsiJavaToken:AT('@')
PsiJavaCodeReferenceElement:Slowpoke
PsiIdentifier:Slowpoke('Slowpoke')
PsiReferenceParameterList
<empty list>
PsiAnnotationParameterList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACKET('[')
PsiJavaToken:RBRACKET(']')
PsiWhiteSpace(' ')
PsiAnnotation
PsiJavaToken:AT('@')
PsiJavaCodeReferenceElement:Slowbro
PsiIdentifier:Slowbro('Slowbro')
PsiReferenceParameterList
<empty list>
PsiAnnotationParameterList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACKET('[')
PsiJavaToken:RBRACKET(']')
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiReturnStatement
PsiKeyword:return('return')
PsiWhiteSpace(' ')
PsiLiteralExpression:null
PsiJavaToken:NULL_KEYWORD('null')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
PsiWhiteSpace('\n\n ')
PsiComment(END_OF_LINE_COMMENT)('//')
PsiWhiteSpace('\n ')
PsiComment(END_OF_LINE_COMMENT)('// 4. A type annotation is permitted in front of a constructor declaration ...')
@@ -380,6 +380,7 @@ public class JavaStubBuilderTest extends LightIdeaTestCase {
" m(@A6 C::m);\n" +
" @A7 T @A8[] @A9[] a = new @A7 T @A8[0] @A9[0];\n" +
" }\n" +
" int @A [] v() @A [] { }\n" +
"}",
"PsiJavaFileStub []\n" +
@@ -447,7 +448,16 @@ public class JavaStubBuilderTest extends LightIdeaTestCase {
" ANNOTATION:PsiAnnotationStub[@A8]\n" +
" ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" +
" ANNOTATION:PsiAnnotationStub[@A9]\n" +
" ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n");
" ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" +
" METHOD:PsiMethodStub[v:int[][]]\n" +
" MODIFIER_LIST:PsiModifierListStub[mask=4096]\n" +
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
" ANNOTATION:PsiAnnotationStub[@A]\n" +
" ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" +
" PARAMETER_LIST:PsiParameterListStub\n" +
" ANNOTATION:PsiAnnotationStub[@A]\n" +
" ANNOTATION_PARAMETER_LIST:PsiAnnotationParameterListStubImpl\n" +
" THROWS_LIST:PsiRefListStub[THROWS_LIST:]\n");
}
public void testSOEProof() {