IDEA-84963 Disprefer annotations in autocomplete at places where they cannot be used

This commit is contained in:
peter
2012-07-24 10:31:15 +02:00
parent 0364eb48a6
commit 42eebc6127
7 changed files with 287 additions and 197 deletions
@@ -18,16 +18,17 @@ package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.openapi.util.Condition;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.filters.getters.MembersGetter;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@@ -55,26 +56,60 @@ public class PreferByKindWeigher extends LookupElementWeigher {
private final CompletionType myCompletionType;
private final PsiElement myPosition;
private final Set<PsiField> myNonInitializedFields;
@Nullable private final String myRequiredSuper;
@NotNull private final Condition<PsiClass> myRequiredSuper;
public PreferByKindWeigher(CompletionType completionType, PsiElement position) {
public PreferByKindWeigher(CompletionType completionType, final PsiElement position) {
super("local");
myCompletionType = completionType;
myPosition = position;
myNonInitializedFields = JavaCompletionProcessor.getNonInitializedFields(position);
myRequiredSuper = createSuitabilityCondition(position);
}
private static Condition<PsiClass> createSuitabilityCondition(final PsiElement position) {
if (IN_CATCH_TYPE.accepts(position) ||
IN_MULTI_CATCH_TYPE.accepts(position) ||
JavaSmartCompletionContributor.AFTER_THROW_NEW.accepts(position) ||
INSIDE_METHOD_THROWS_CLAUSE.accepts(position)) {
myRequiredSuper = CommonClassNames.JAVA_LANG_THROWABLE;
return new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
return InheritanceUtil.isInheritor(psiClass, CommonClassNames.JAVA_LANG_THROWABLE);
}
};
}
else if (IN_RESOURCE_TYPE.accepts(position)) {
myRequiredSuper = CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE;
if (IN_RESOURCE_TYPE.accepts(position)) {
return new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
return InheritanceUtil.isInheritor(psiClass, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE);
}
};
}
else {
myRequiredSuper = null;
if (psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class).accepts(position)) {
final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(position, PsiAnnotation.class);
assert annotation != null;
PsiAnnotationOwner owner = annotation.getOwner();
if (owner instanceof PsiModifierList || owner instanceof PsiTypeElement ||
owner instanceof PsiMethodReceiver || owner instanceof PsiTypeParameter) {
PsiElement member = ((PsiElement)owner).getParent();
final String[] elementTypeFields = PsiAnnotationImpl
.getApplicableElementTypeFields(owner instanceof PsiModifierList ? member : (PsiElement)owner);
return new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
if (!psiClass.isAnnotationType()) {
return false;
}
return PsiAnnotationImpl.isAnnotationApplicable(false, psiClass, elementTypeFields, position.getResolveScope());
}
};
}
}
//noinspection unchecked
return Condition.FALSE;
}
enum MyResult {
@@ -156,7 +191,7 @@ public class PreferByKindWeigher extends LookupElementWeigher {
}
if (object instanceof PsiClass) {
if (myRequiredSuper != null && InheritanceUtil.isInheritor((PsiClass)object, myRequiredSuper)) {
if (myRequiredSuper.value((PsiClass)object)) {
return MyResult.suitableClass;
}
return MyResult.classNameOrGlobalStatic;
@@ -162,14 +162,17 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
return !strict;
}
PsiClass annotationType = (PsiClass)resolved;
return isAnnotationApplicable(strict, annotationType, elementTypeFields, annotation.getManager(), annotation.getResolveScope());
return isAnnotationApplicable(strict, annotationType, elementTypeFields, annotation.getResolveScope());
}
public static boolean isAnnotationApplicable(boolean strict,
PsiClass annotationType,
String[] elementTypeFields,
PsiManager manager,
@NotNull PsiClass annotationType,
@Nullable String[] elementTypeFields,
GlobalSearchScope resolveScope) {
if (elementTypeFields == null) {
return !strict;
}
PsiAnnotation target = annotationType.getModifierList().findAnnotation(CommonClassNames.TARGET_ANNOTATION_FQ_NAME);
if (target == null) {
//todo hack: ambiguity in spec
@@ -183,7 +186,8 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
PsiAnnotationMemberValue value = attributes[0].getValue();
LOG.assertTrue(elementTypeFields.length > 0);
PsiClass elementTypeClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.annotation.ElementType", resolveScope);
PsiClass elementTypeClass =
JavaPsiFacade.getInstance(annotationType.getProject()).findClass("java.lang.annotation.ElementType", resolveScope);
if (elementTypeClass == null) {
//todo hack
return !strict;
@@ -192,17 +196,23 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
for (String fieldName : elementTypeFields) {
PsiField field = elementTypeClass.findFieldByName(fieldName, false);
if (field == null) continue;
if (field == null) {
continue;
}
if (value instanceof PsiArrayInitializerMemberValue) {
PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)value).getInitializers();
for (PsiAnnotationMemberValue initializer : initializers) {
if (initializer instanceof PsiReference) {
if (((PsiReference)initializer).isReferenceTo(field)) return true;
if (((PsiReference)initializer).isReferenceTo(field)) {
return true;
}
}
}
}
else if (value instanceof PsiReference) {
if (((PsiReference)value).isReferenceTo(field)) return true;
if (((PsiReference)value).isReferenceTo(field)) {
return true;
}
}
}
return false;
@@ -0,0 +1,12 @@
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Z<caret>
@interface Foo {
}
@Target({ElementType.ANNOTATION_TYPE})
@interface ZMetaAnno {}
@Target({ElementType.LOCAL_VARIABLE})
@interface ZLocalAnno {}
@@ -342,6 +342,10 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
checkPreferredItems 0, 'serial', 'superExpressionInIllegalContext'
}
public void testPreferApplicableAnnotations() throws Throwable {
checkPreferredItems 0, 'ZMetaAnno', 'ZLocalAnno'
}
public void testJComponentAddNewWithStats() throws Throwable {
final LookupImpl lookup = invokeCompletion("/../smartTypeSorting/JComponentAddNew.java");
assertPreferredItems(0, "FooBean3", "JComponent", "Component");
@@ -1,179 +1,190 @@
/*
* Copyright 2000-2010 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 org.jetbrains.plugins.groovy.lang.completion.weighers;
import com.intellij.codeInsight.completion.CompletionLocation;
import com.intellij.codeInsight.completion.CompletionWeigher;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionContributor;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrPropertyForCompletion;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
public class GrKindWeigher extends CompletionWeigher {
private static final Set<String> TRASH_CLASSES = new HashSet<String>(10);
private static final Set<String> PRIORITY_KEYWORDS = CollectionFactory.hashSet(
PsiKeyword.RETURN, PsiKeyword.INSTANCEOF, "in",
PsiKeyword.PRIVATE, PsiKeyword.PROTECTED, PsiKeyword.PUBLIC, PsiKeyword.STATIC, "def",
PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL);
static {
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_CLASS);
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_OBJECT);
TRASH_CLASSES.add(GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT);
}
@Override
public Comparable weigh(@NotNull LookupElement element, @NotNull CompletionLocation location) {
final PsiElement position = location.getCompletionParameters().getPosition();
if (!(position.getContainingFile() instanceof GroovyFileBase)) return null;
Object o = element.getObject();
if (o instanceof ResolveResult) {
o = ((ResolveResult)o).getElement();
}
final PsiElement parent = position.getParent();
final PsiElement qualifier = parent instanceof GrReferenceElement ? ((GrReferenceElement)parent).getQualifier() : null;
if (qualifier == null) {
if (o instanceof NamedArgumentDescriptor) {
switch (((NamedArgumentDescriptor)o).getPriority()) {
case ALWAYS_ON_TOP:
return NotQualifiedKind.onTop;
case AS_LOCAL_VARIABLE:
return NotQualifiedKind.local;
default:
return NotQualifiedKind.unknown;
}
}
if (o instanceof PsiVariable && !(o instanceof PsiField)) {
return NotQualifiedKind.local;
}
PsiTypeLookupItem item = element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY);
if (item != null && item.getBracketsCount() > 0) {
return NotQualifiedKind.arrayType;
}
if (isPriorityKeyword(o)) return NotQualifiedKind.local;
if (isLightElement(o)) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) {
if (((PsiClass)o).isAnnotationType() && GroovyCompletionContributor.AFTER_AT.accepts(position)) {
return NotQualifiedKind.restrictedClass;
}
if (GroovyCompletionContributor.IN_CATCH_TYPE.accepts(position) &&
InheritanceUtil.isInheritor((PsiClass)o, CommonClassNames.JAVA_LANG_THROWABLE)) {
return NotQualifiedKind.restrictedClass;
}
}
if (o instanceof PsiMember) {
final PsiClass containingClass = ((PsiMember)o).getContainingClass();
if (isAccessor((PsiMember)o)) return NotQualifiedKind.accessor;
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) return NotQualifiedKind.innerClass;
if (PsiTreeUtil.isContextAncestor(containingClass, position, false)) return NotQualifiedKind.currentClassMember;
return NotQualifiedKind.member;
}
return NotQualifiedKind.unknown;
}
else {
if (o instanceof PsiEnumConstant) return QualifiedKind.enumConstant;
if (isLightElement(o)) return QualifiedKind.unknown;
if (o instanceof PsiMember) {
if (isTrashMethod((PsiMember)o)) return QualifiedKind.unknown;
if (isAccessor((PsiMember)o)) return QualifiedKind.accessor;
if (isQualifierClassMember((PsiMember)o, qualifier)) {
return QualifiedKind.currentClassMember;
}
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return QualifiedKind.unknown;
if (o instanceof PsiClass) return QualifiedKind.innerClass;
return QualifiedKind.member;
}
return QualifiedKind.unknown;
}
}
private static boolean isPriorityKeyword(Object o) {
//noinspection SuspiciousMethodCalls
return PRIORITY_KEYWORDS.contains(o);
}
private static boolean isLightElement(Object o) {
return o instanceof LightElement && !(o instanceof GrPropertyForCompletion) && !(o instanceof GrAccessorMethod);
}
private static boolean isTrashMethod(PsiMember o) {
final PsiClass containingClass = o.getContainingClass();
return containingClass != null && TRASH_CLASSES.contains(containingClass.getQualifiedName());
}
private static boolean isAccessor(PsiMember member) {
return member instanceof PsiMethod && (GroovyPropertyUtils.isSimplePropertyAccessor((PsiMethod)member) || "setProperty".equals(((PsiMethod)member).getName()));
}
private static boolean isQualifierClassMember(PsiMember member, PsiElement qualifier) {
if (!(qualifier instanceof GrExpression)) return false;
final PsiType type = ((GrExpression)qualifier).getType();
if (!(type instanceof PsiClassType)) return false;
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass == null) return false;
return qualifier.getManager().areElementsEquivalent(member.getContainingClass(), psiClass);
}
private enum NotQualifiedKind {
arrayType,
innerClass,
unknown,
accessor,
member,
currentClassMember,
restrictedClass,
local,
onTop
}
private enum QualifiedKind {
innerClass,
unknown,
accessor,
member,
currentClassMember,
enumConstant,
}
}
/*
* Copyright 2000-2010 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 org.jetbrains.plugins.groovy.lang.completion.weighers;
import com.intellij.codeInsight.completion.CompletionLocation;
import com.intellij.codeInsight.completion.CompletionWeigher;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionContributor;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrPropertyForCompletion;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
public class GrKindWeigher extends CompletionWeigher {
private static final Set<String> TRASH_CLASSES = new HashSet<String>(10);
private static final Set<String> PRIORITY_KEYWORDS = CollectionFactory.hashSet(
PsiKeyword.RETURN, PsiKeyword.INSTANCEOF, "in",
PsiKeyword.PRIVATE, PsiKeyword.PROTECTED, PsiKeyword.PUBLIC, PsiKeyword.STATIC, "def",
PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL);
static {
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_CLASS);
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_OBJECT);
TRASH_CLASSES.add(GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT);
}
@Override
public Comparable weigh(@NotNull LookupElement element, @NotNull CompletionLocation location) {
final PsiElement position = location.getCompletionParameters().getPosition();
if (!(position.getContainingFile() instanceof GroovyFileBase)) return null;
Object o = element.getObject();
if (o instanceof ResolveResult) {
o = ((ResolveResult)o).getElement();
}
final PsiElement parent = position.getParent();
final PsiElement qualifier = parent instanceof GrReferenceElement ? ((GrReferenceElement)parent).getQualifier() : null;
if (qualifier == null) {
if (o instanceof NamedArgumentDescriptor) {
switch (((NamedArgumentDescriptor)o).getPriority()) {
case ALWAYS_ON_TOP:
return NotQualifiedKind.onTop;
case AS_LOCAL_VARIABLE:
return NotQualifiedKind.local;
default:
return NotQualifiedKind.unknown;
}
}
if (o instanceof PsiVariable && !(o instanceof PsiField)) {
return NotQualifiedKind.local;
}
PsiTypeLookupItem item = element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY);
if (item != null && item.getBracketsCount() > 0) {
return NotQualifiedKind.arrayType;
}
if (isPriorityKeyword(o)) return NotQualifiedKind.local;
if (isLightElement(o)) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) {
if (((PsiClass)o).isAnnotationType() && GroovyCompletionContributor.AFTER_AT.accepts(position)) {
final GrAnnotation annotation = PsiTreeUtil.getParentOfType(position, GrAnnotation.class);
if (annotation != null) {
PsiElement annoParent = annotation.getParent();
PsiElement owner = annoParent.getParent();
String[] elementTypeFields = GrAnnotationImpl.getApplicableElementTypeFields(annoParent instanceof PsiModifierList ? owner : annoParent);
if (PsiAnnotationImpl.isAnnotationApplicable(false, (PsiClass)o, elementTypeFields, position.getResolveScope())) {
return NotQualifiedKind.restrictedClass;
}
}
}
if (GroovyCompletionContributor.IN_CATCH_TYPE.accepts(position) &&
InheritanceUtil.isInheritor((PsiClass)o, CommonClassNames.JAVA_LANG_THROWABLE)) {
return NotQualifiedKind.restrictedClass;
}
}
if (o instanceof PsiMember) {
final PsiClass containingClass = ((PsiMember)o).getContainingClass();
if (isAccessor((PsiMember)o)) return NotQualifiedKind.accessor;
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) return NotQualifiedKind.innerClass;
if (PsiTreeUtil.isContextAncestor(containingClass, position, false)) return NotQualifiedKind.currentClassMember;
return NotQualifiedKind.member;
}
return NotQualifiedKind.unknown;
}
else {
if (o instanceof PsiEnumConstant) return QualifiedKind.enumConstant;
if (isLightElement(o)) return QualifiedKind.unknown;
if (o instanceof PsiMember) {
if (isTrashMethod((PsiMember)o)) return QualifiedKind.unknown;
if (isAccessor((PsiMember)o)) return QualifiedKind.accessor;
if (isQualifierClassMember((PsiMember)o, qualifier)) {
return QualifiedKind.currentClassMember;
}
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return QualifiedKind.unknown;
if (o instanceof PsiClass) return QualifiedKind.innerClass;
return QualifiedKind.member;
}
return QualifiedKind.unknown;
}
}
private static boolean isPriorityKeyword(Object o) {
//noinspection SuspiciousMethodCalls
return PRIORITY_KEYWORDS.contains(o);
}
private static boolean isLightElement(Object o) {
return o instanceof LightElement && !(o instanceof GrPropertyForCompletion) && !(o instanceof GrAccessorMethod);
}
private static boolean isTrashMethod(PsiMember o) {
final PsiClass containingClass = o.getContainingClass();
return containingClass != null && TRASH_CLASSES.contains(containingClass.getQualifiedName());
}
private static boolean isAccessor(PsiMember member) {
return member instanceof PsiMethod && (GroovyPropertyUtils.isSimplePropertyAccessor((PsiMethod)member) || "setProperty".equals(((PsiMethod)member).getName()));
}
private static boolean isQualifierClassMember(PsiMember member, PsiElement qualifier) {
if (!(qualifier instanceof GrExpression)) return false;
final PsiType type = ((GrExpression)qualifier).getType();
if (!(type instanceof PsiClassType)) return false;
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass == null) return false;
return qualifier.getManager().areElementsEquivalent(member.getContainingClass(), psiClass);
}
private enum NotQualifiedKind {
arrayType,
innerClass,
unknown,
accessor,
member,
currentClassMember,
restrictedClass,
local,
onTop
}
private enum QualifiedKind {
innerClass,
unknown,
accessor,
member,
currentClassMember,
enumConstant,
}
}
@@ -179,7 +179,7 @@ public class GrAnnotationImpl extends GrStubElementBase<GrAnnotationStub> implem
GrCodeReferenceElement nameRef = annotation.getClassReference();
PsiElement resolved = nameRef.resolve();
if (resolved instanceof PsiClass && ((PsiClass)resolved).isAnnotationType()) {
return PsiAnnotationImpl.isAnnotationApplicable(strict, (PsiClass)resolved, elementTypeFields, annotation.getManager(),annotation.getResolveScope());
return PsiAnnotationImpl.isAnnotationApplicable(strict, (PsiClass)resolved, elementTypeFields, annotation.getResolveScope());
}
return !strict;
}
@@ -1398,4 +1398,22 @@ def bar(){}''')
assertOrderedEquals(myFixture.lookupElementStrings, ['fooo', 'fooo1'])
}
public void testPreferApplicableAnnotations() {
configure('''
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Z<caret>
@interface Foo {
}
@Target(ElementType.ANNOTATION_TYPE)
@interface ZMetaAnno {}
@Target(ElementType.LOCAL_VARIABLE)
@interface ZLocalAnno {}''')
myFixture.completeBasic()
myFixture.assertPreferredCompletionItems 0, 'ZMetaAnno', 'ZLocalAnno'
}
}