IDEA-99589 Groovy 2.1 : highlighting for attributes of annotation aliases

This commit is contained in:
Max Medvedev
2013-01-28 13:13:22 +04:00
parent f15aa5d242
commit 6569ef7a75
6 changed files with 312 additions and 134 deletions
@@ -17,13 +17,17 @@ package org.jetbrains.plugins.groovy.annotator;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import java.util.Map;
/**
* @author Medvdedev Max
*/
@@ -31,6 +35,26 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
public class AnnotationCollectorChecker extends CustomAnnotationChecker {
@Override
public boolean checkApplicability(@NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
return isInAliasDeclaration(annotation);
}
@Override
public boolean checkArgumentList(@NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
if (!isInAliasDeclaration(annotation)) return false;
Map<PsiElement, String> errors = ContainerUtil.newHashMap();
final PsiClass clazz = (PsiClass)annotation.getClassReference().resolve();
if (clazz == null) return true;
final GrAnnotationNameValuePair[] attributes = annotation.getParameterList().getAttributes();
CustomAnnotationChecker.checkAnnotationArguments(errors, clazz, annotation.getClassReference(), attributes, false);
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
holder.createErrorAnnotation(entry.getKey(), entry.getValue());
}
return true;
}
private static boolean isInAliasDeclaration(GrAnnotation annotation) {
final PsiElement parent = annotation.getParent();
if (parent instanceof GrModifierList) {
final GrAnnotation collector = ContainerUtil.find(((GrModifierList)parent).getRawAnnotations(), new Condition<GrAnnotation>() {
@@ -18,13 +18,28 @@ package org.jetbrains.plugins.groovy.annotator;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifierList;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Max Medvedev
@@ -51,4 +66,131 @@ public abstract class CustomAnnotationChecker {
return null;
}
public static void checkAnnotationArguments(@NotNull Map<PsiElement, String> holder,
@NotNull PsiClass annotation,
@NotNull GrCodeReferenceElement refToHighlight,
@NotNull GrAnnotationNameValuePair[] attributes, boolean checkMissedAttributes) {
Set<String> usedAttrs = new HashSet<String>();
if (attributes.length > 0) {
final PsiElement identifier = attributes[0].getNameIdentifierGroovy();
if (attributes.length == 1 && identifier == null) {
checkAnnotationValue(annotation, attributes[0], "value", usedAttrs, attributes[0].getValue(), holder);
}
else {
for (GrAnnotationNameValuePair attribute : attributes) {
final String name = attribute.getName();
if (name != null) {
final PsiElement toHighlight = attribute.getNameIdentifierGroovy();
assert toHighlight != null;
checkAnnotationValue(annotation, toHighlight, name, usedAttrs, attribute.getValue(), holder);
}
}
}
}
List<String> missedAttrs = new ArrayList<String>();
final PsiMethod[] methods = annotation.getMethods();
for (PsiMethod method : methods) {
final String name = method.getName();
if (usedAttrs.contains(name) ||
method instanceof PsiAnnotationMethod && ((PsiAnnotationMethod)method).getDefaultValue() != null) {
continue;
}
missedAttrs.add(name);
}
if (checkMissedAttributes && !missedAttrs.isEmpty()) {
holder.put(refToHighlight, GroovyBundle.message("missed.attributes", StringUtil.join(missedAttrs, ", ")));
}
}
private static void checkAnnotationValue(@NotNull PsiClass annotation,
@NotNull PsiElement identifierToHighlight,
@NotNull String name,
@NotNull Set<String> usedAttrs,
@Nullable GrAnnotationMemberValue value,
@NotNull Map<PsiElement, String> holder) {
if (usedAttrs.contains(name)) {
holder.put(identifierToHighlight, GroovyBundle.message("duplicate.attribute"));
}
usedAttrs.add(name);
final PsiMethod[] methods = annotation.findMethodsByName(name, false);
if (methods.length == 0) {
holder.put(identifierToHighlight,
GroovyBundle.message("at.interface.0.does.not.contain.attribute", annotation.getQualifiedName(), name));
}
else {
final PsiMethod method = methods[0];
final PsiType ltype = method.getReturnType();
if (ltype != null && value != null) {
checkAnnotationValueByType(holder, value, ltype, true);
}
}
}
public static void checkAnnotationValueByType(@NotNull Map<PsiElement, String> holder,
@NotNull GrAnnotationMemberValue value,
@Nullable PsiType ltype,
boolean skipArrays) {
final GlobalSearchScope resolveScope = value.getResolveScope();
final PsiManager manager = value.getManager();
if (value instanceof GrExpression) {
final PsiType rtype;
if (value instanceof GrClosableBlock) {
rtype = PsiType.getJavaLangClass(manager, resolveScope);
}
else {
rtype = ((GrExpression)value).getType();
}
if (rtype != null && !checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
holder.put(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
}
else if (value instanceof GrAnnotation) {
final PsiElement resolved = ((GrAnnotation)value).getClassReference().resolve();
if (resolved instanceof PsiClass) {
final PsiClassType rtype = JavaPsiFacade.getElementFactory(value.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
if (!checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
holder.put(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
}
}
else if (value instanceof GrAnnotationArrayInitializer) {
if (ltype instanceof PsiArrayType) {
final PsiType componentType = ((PsiArrayType)ltype).getComponentType();
final GrAnnotationMemberValue[] initializers = ((GrAnnotationArrayInitializer)value).getInitializers();
for (GrAnnotationMemberValue initializer : initializers) {
checkAnnotationValueByType(holder, initializer, componentType, false);
}
}
else {
final PsiType rtype = TypesUtil.getTupleByAnnotationArrayInitializer((GrAnnotationArrayInitializer)value);
if (!checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
holder.put(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
}
}
}
private static boolean checkAnnoTypeAssignable(@Nullable PsiType type,
@Nullable PsiType rtype,
@NotNull GroovyPsiElement context,
boolean skipArrays) {
rtype = TypesUtil.unboxPrimitiveTypeWrapper(rtype);
if (TypesUtil.isAssignableByMethodCallConversion(type, rtype, context)) return true;
if (!(type instanceof PsiArrayType && skipArrays)) return false;
final PsiType componentType = ((PsiArrayType)type).getComponentType();
return checkAnnoTypeAssignable(componentType, rtype, context, skipArrays);
}
}
@@ -16,16 +16,25 @@
package org.jetbrains.plugins.groovy.annotator;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrAnnotationCollector;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
/**
* @author Max Medvedev
*/
@@ -46,15 +55,14 @@ public class GrAliasAnnotationChecker extends CustomAnnotationChecker {
@Override
public boolean checkApplicability(@NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
final GrCodeReferenceElement ref = annotation.getClassReference();
final GrTypeDefinition resolved = resolveAlias(annotation);
if (resolved == null) {
final GrTypeDefinition alias = resolveAlias(annotation);
if (alias == null) {
return false;
}
final GrModifierList list = resolved.getModifierList();
final GrCodeReferenceElement ref = annotation.getClassReference();
final GrModifierList list = alias.getModifierList();
assert list != null;
for (GrAnnotation anno : list.getRawAnnotations()) {
if (GroovyCommonClassNames.GROOVY_TRANSFORM_ANNOTATION_COLLECTOR.equals(anno.getQualifiedName())) continue;
@@ -69,11 +77,49 @@ public class GrAliasAnnotationChecker extends CustomAnnotationChecker {
@Override
public boolean checkArgumentList(@NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
if (resolveAlias(annotation) != null) {
return true;
final GrTypeDefinition alias = resolveAlias(annotation);
if (alias == null) {
return false;
}
final ArrayList<GrAnnotation> annotations = ContainerUtil.newArrayList();
final GrAnnotation annotationCollector = GrAnnotationCollector.findAnnotationCollector(alias);
assert annotationCollector != null;
final Set<String> usedAttributes = GrAnnotationCollector.collectAnnotations(annotations, annotation, annotationCollector);
return false;
final GrCodeReferenceElement ref = annotation.getClassReference();
Map<PsiElement, String> map = ContainerUtil.newHashMap();
for (GrAnnotation aliased : annotations) {
final PsiClass clazz = (PsiClass)aliased.getClassReference().resolve();
assert clazz != null;
checkAnnotationArguments(map, clazz, ref, aliased.getParameterList().getAttributes(), true);
}
for (Map.Entry<PsiElement, String> entry : map.entrySet()) {
final PsiElement key = entry.getKey();
final String value = entry.getValue();
if (PsiTreeUtil.isAncestor(annotation, key, true)) {
holder.createErrorAnnotation(key, value);
}
else {
holder.createErrorAnnotation(ref, value);
}
}
final GrAnnotationNameValuePair[] attributes = annotation.getParameterList().getAttributes();
final String aliasQName = annotation.getQualifiedName();
if (attributes.length == 1 && attributes[0].getNameIdentifierGroovy() == null && !usedAttributes.contains("value")) {
holder.createErrorAnnotation(attributes[0], GroovyBundle.message("at.interface.0.does.not.contain.attribute", aliasQName, "value"));
}
for (GrAnnotationNameValuePair pair : attributes) {
final PsiElement nameIdentifier = pair.getNameIdentifierGroovy();
if (nameIdentifier != null && !usedAttributes.contains(pair.getName())) {
holder.createErrorAnnotation(nameIdentifier, GroovyBundle.message("at.interface.0.does.not.contain.attribute", aliasQName, pair.getName()));
}
}
return true;
}
}
@@ -34,7 +34,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
@@ -60,7 +59,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.*;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
@@ -86,7 +87,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.*;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrScriptField;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
@@ -1340,59 +1340,11 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
if (checker.checkArgumentList(myHolder, annotation)) return;
}
final GrAnnotationNameValuePair[] attributes = annotationArgumentList.getAttributes();
Set<String> usedAttrs = new HashSet<String>();
if (attributes.length == 1 && attributes[0].getNameIdentifierGroovy() == null) {
checkAnnotationValue(anno, attributes[0], "value", usedAttrs, attributes[0].getValue());
}
else {
for (GrAnnotationNameValuePair attribute : attributes) {
final PsiElement identifier = attribute.getNameIdentifierGroovy();
final String name = identifier.getText();
checkAnnotationValue(anno, identifier, name, usedAttrs, attribute.getValue());
}
}
List<String> missedAttrs = new ArrayList<String>();
final PsiMethod[] methods = anno.getMethods();
for (PsiMethod method : methods) {
final String name = method.getName();
if (usedAttrs.contains(name) ||
method instanceof PsiAnnotationMethod && ((PsiAnnotationMethod)method).getDefaultValue() != null) {
continue;
}
missedAttrs.add(name);
}
if (!missedAttrs.isEmpty()) {
myHolder.createErrorAnnotation(annotation.getClassReference(),
GroovyBundle.message("missed.attributes", StringUtil.join(missedAttrs, ", ")));
}
}
private void checkAnnotationValue(@NotNull PsiClass anno,
@NotNull PsiElement identifierToHighlight,
@NotNull String name,
@NotNull Set<String> usedAttrs,
@Nullable GrAnnotationMemberValue value) {
if (usedAttrs.contains(name)) {
myHolder.createErrorAnnotation(identifierToHighlight, GroovyBundle.message("duplicate.attribute"));
}
usedAttrs.add(name);
final PsiMethod[] methods = anno.findMethodsByName(name, false);
if (methods.length == 0) {
myHolder.createErrorAnnotation(identifierToHighlight,
GroovyBundle.message("at.interface.0.does.not.contain.attribute", anno.getQualifiedName(), name));
}
else {
final PsiMethod method = methods[0];
final PsiType ltype = method.getReturnType();
if (ltype != null && value != null) {
checkAnnotationValueByType(value, ltype, true);
}
Map<PsiElement, String> errors = ContainerUtil.newHashMap();
CustomAnnotationChecker.checkAnnotationArguments(errors, anno, annotation.getClassReference(), annotationArgumentList.getAttributes(),
true);
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
myHolder.createErrorAnnotation(entry.getKey(), entry.getValue());
}
}
@@ -1405,69 +1357,11 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
assert parent instanceof GrAnnotationMethod;
final PsiType type = ((GrAnnotationMethod)parent).getReturnType();
checkAnnotationValueByType(value, type, false);
}
private void checkAnnotationValueByType(@NotNull GrAnnotationMemberValue value, @Nullable PsiType ltype, boolean skipArrays) {
final GlobalSearchScope resolveScope = value.getResolveScope();
final PsiManager manager = value.getManager();
if (value instanceof GrExpression) {
final PsiType rtype;
if (value instanceof GrClosableBlock) {
rtype = PsiType.getJavaLangClass(manager, resolveScope);
}
else {
rtype = ((GrExpression)value).getType();
}
if (rtype != null && !checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
myHolder
.createErrorAnnotation(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
Map<PsiElement, String> errors = ContainerUtil.newHashMap();
CustomAnnotationChecker.checkAnnotationValueByType(errors, value, type, false);
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
myHolder.createErrorAnnotation(entry.getKey(), entry.getValue());
}
else if (value instanceof GrAnnotation) {
final PsiElement resolved = ((GrAnnotation)value).getClassReference().resolve();
if (resolved instanceof PsiClass) {
final PsiClassType rtype = JavaPsiFacade.getElementFactory(value.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
if (!checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
myHolder
.createErrorAnnotation(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
}
}
else if (value instanceof GrAnnotationArrayInitializer) {
if (ltype instanceof PsiArrayType) {
final PsiType componentType = ((PsiArrayType)ltype).getComponentType();
final GrAnnotationMemberValue[] initializers = ((GrAnnotationArrayInitializer)value).getInitializers();
for (GrAnnotationMemberValue initializer : initializers) {
checkAnnotationValueByType(initializer, componentType, false);
}
}
else {
final PsiType rtype = TypesUtil.getTupleByAnnotationArrayInitializer((GrAnnotationArrayInitializer)value);
if (!checkAnnoTypeAssignable(ltype, rtype, value, skipArrays)) {
myHolder
.createErrorAnnotation(value, GroovyBundle.message("cannot.assign", rtype.getPresentableText(), ltype.getPresentableText()));
}
}
}
}
private static boolean checkAnnoTypeAssignable(@Nullable PsiType type,
@Nullable PsiType rtype,
@NotNull GroovyPsiElement context,
boolean skipArrays) {
rtype = TypesUtil.unboxPrimitiveTypeWrapper(rtype);
if (TypesUtil.isAssignableByMethodCallConversion(type, rtype, context)) return true;
if (!(type instanceof PsiArrayType && skipArrays)) return false;
final PsiType componentType = ((PsiArrayType)type).getComponentType();
return checkAnnoTypeAssignable(componentType, rtype, context, skipArrays);
}
@Override
@@ -68,9 +68,16 @@ public class GrAnnotationCollector {
return false;
}
public static void collectAnnotations(@NotNull List<GrAnnotation> list,
@NotNull GrAnnotation alias,
@NotNull GrAnnotation annotationCollector) {
/**
*
* @param list resulting collection of aliased annotations
* @param alias alias annotation
* @param annotationCollector @AnnotationCollector annotation used in alias declaration
* @return set of used arguments of alias annotation
*/
public static Set<String> collectAnnotations(@NotNull List<GrAnnotation> list,
@NotNull GrAnnotation alias,
@NotNull GrAnnotation annotationCollector) {
final GrModifierList modifierList = (GrModifierList)annotationCollector.getParent();
@@ -83,6 +90,7 @@ public class GrAnnotationCollector {
final PsiManager manager = alias.getManager();
final GrAnnotationNameValuePair[] attributes = alias.getParameterList().getAttributes();
Set<String> allUsedAttrs = ContainerUtil.newHashSet();
for (Map.Entry<String, Map<String, GrAnnotationNameValuePair>> entry : annotations.entrySet()) {
final String qname = entry.getKey();
final PsiClass resolved = JavaPsiFacade.getInstance(alias.getProject()).findClass(qname, alias.getResolveScope());
@@ -95,9 +103,9 @@ public class GrAnnotationCollector {
final String name = attr.getName() != null ? attr.getName() : "value";
if (resolved.findMethodsByName(name, false).length > 0) {
annotation.addAttribute(attr);
allUsedAttrs.add(name);
usedAttrs.add(name);
}
usedAttrs.add(name);
}
@@ -111,6 +119,8 @@ public class GrAnnotationCollector {
list.add(annotation);
}
return allUsedAttrs;
}
private static void collectAliasedAnnotationsFromAnnotationCollectorAnnotations(GrModifierList modifierList,
@@ -54,6 +54,25 @@ class F<caret>oo {
''')
}
void testAliasWithMissedProperty() {
testHighlighting('''\
import groovy.transform.*
@interface X {
String[] excludes()
}
@ToString(excludes = ['a', 'b'])
@AnnotationCollector([X, Immutable])
@interface Alias {}
@<error descr="Missed attributes: excludes">Alias</error>
class F<caret>oo {
Integer a, b
}
''')
}
void testInapplicableAlias() {
testHighlighting('''\
import groovy.transform.*
@@ -75,6 +94,49 @@ import groovy.transform.*
@AnnotationCollector([ToString, Immutable])
@Field
@interface Alias {}
''')
}
void testInapplicableAttributeInAliasDeclaration() {
testHighlighting('''\
import groovy.transform.*
@ToString(excludes = ['a', 'b'], <error descr="@interface 'groovy.transform.ToString' does not contain attribute 'foo'">foo</error> = 4)
@AnnotationCollector([EqualsAndHashCode, Immutable])
@interface Alias {}
@Alias
class Foo{}
''')
}
void testUnknownAttributeInAliasUsage() {
testHighlighting('''\
import groovy.transform.*
@ToString(excludes = ['a', 'b'])
@AnnotationCollector([EqualsAndHashCode, Immutable])
@interface Alias {}
@Alias(<error descr="@interface 'Alias' does not contain attribute 'foo'">foo</error> = 5)
class Foo {
Integer a, b
}
''')
}
void testInapplicableAttributeInAliasUsage() {
testHighlighting('''\
import groovy.transform.*
@ToString(excludes = ['a', 'b'])
@AnnotationCollector([EqualsAndHashCode, Immutable])
@interface Alias {}
@Alias(excludes = <error descr="Cannot assign 'Integer' to 'String[]'">5</error>)
class Foo {
Integer a, b
}
''')
}
}