mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-99589 Groovy 2.1 : highlighting for attributes of annotation aliases
This commit is contained in:
+24
@@ -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>() {
|
||||
|
||||
+144
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+55
-9
@@ -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
|
||||
|
||||
+15
-5
@@ -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,
|
||||
|
||||
+62
@@ -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
|
||||
}
|
||||
''')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user