IDEA-101798 DelegatesTo : report unused @DelegatesTo.Target annotations

This commit is contained in:
Max Medvedev
2013-05-17 09:50:04 +04:00
parent 7799900a80
commit 5a42e34091
7 changed files with 192 additions and 14 deletions
@@ -0,0 +1,7 @@
<html>
<body>
<font face="verdana" size="-1">
This inspection checks @groovy.lang.DelegatesTo annotation arguments.
</font>
</body>
</html>
+4
View File
@@ -994,6 +994,10 @@
groupName="Annotations verifying" enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.annotator.inspections.GroovySingletonAnnotationInspection"/>
<localInspection language="Groovy" groupPath="Groovy" shortName="DelegatesTo" displayName="@DelegatesTo inspection"
groupName="Annotations verifying" enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.codeInspection.confusing.DelegatesToInspection"/>
<implicitUsageProvider implementation="org.jetbrains.plugins.groovy.gpp.GppImplicitUsageProvider"/>
<implicitUsageProvider implementation="org.jetbrains.plugins.groovy.findUsages.GrImplicitUsageProvider"/>
@@ -102,4 +102,6 @@ gr.package=Package mismatch
java.style.property.access=Java-style accessor invocation
type.customizer.is.not.marked.as.a.resource.file=Type customizer script is not marked as compiler resources
add.to.resources=Add to resources
add.type.customizer.to.resources=Add type customizer script to resources
add.type.customizer.to.resources=Add type customizer script to resources
target.0.does.not.exist=Target ''{0}'' does not exist
target.annotation.is.unused=@Target is unused
@@ -0,0 +1,108 @@
/*
* 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 org.jetbrains.plugins.groovy.codeInspection.confusing;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiElement;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
/**
* @author Max Medvedev
*/
public class DelegatesToInspection extends BaseInspection {
@NotNull
@Override
protected BaseInspectionVisitor buildVisitor() {
return new BaseInspectionVisitor() {
@Override
public void visitAnnotation(GrAnnotation annotation) {
checkTarget(annotation);
checkDelegatesTo(annotation);
}
private void checkTarget(GrAnnotation annotation) {
if (!GroovyCommonClassNames.GROOVY_LANG_DELEGATES_TO_TARGET.equals(annotation.getQualifiedName())) return;
final PsiElement owner = annotation.getParent().getParent();
if (!(owner instanceof GrParameter)) return;
final boolean isTargetDeclared = annotation.findDeclaredAttributeValue("value") != null;
String targetName = GrAnnotationUtil.inferStringAttribute(annotation, "value");
final GrParameterList parameterList = DefaultGroovyMethods.asType(owner.getParent(), GrParameterList.class);
for (GrParameter parameter : parameterList.getParameters()) {
final PsiAnnotation delegatesTo = parameter.getModifierList().findAnnotation(GroovyCommonClassNames.GROOVY_LANG_DELEGATES_TO);
if (delegatesTo != null) {
if (isTargetDeclared) {
final String curTarget = GrAnnotationUtil.inferStringAttribute(delegatesTo, "target");
if (curTarget != null && curTarget.equals(targetName)) {
return; //target is used
}
}
else {
if (delegatesTo.findDeclaredAttributeValue("target") == null) {
return; // target is used
}
}
}
}
registerError(annotation.getClassReference(), GroovyInspectionBundle.message("target.annotation.is.unused"),
LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
private void checkDelegatesTo(GrAnnotation annotation) {
if (!GroovyCommonClassNames.GROOVY_LANG_DELEGATES_TO.equals(annotation.getQualifiedName())) return;
final PsiElement owner = annotation.getParent().getParent();
if (!(owner instanceof GrParameter)) return;
final PsiAnnotationMemberValue targetPair = annotation.findDeclaredAttributeValue("target");
if (targetPair == null) return;
String targetName = GrAnnotationUtil.inferStringAttribute(annotation, "target");
final GrParameterList parameterList = DefaultGroovyMethods.asType(owner.getParent(), GrParameterList.class);
for (GrParameter parameter : parameterList.getParameters()) {
final PsiAnnotation target = parameter.getModifierList().findAnnotation(GroovyCommonClassNames.GROOVY_LANG_DELEGATES_TO_TARGET);
if (target != null) {
final String curTarget = GrAnnotationUtil.inferStringAttribute(target, "value");
if (curTarget != null && curTarget.equals(targetName)) {
return; //target is used
}
}
}
registerError(targetPair, GroovyInspectionBundle.message("target.0.does.not.exist", targetName != null ? targetName : "?"),
LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
};
}
}
@@ -0,0 +1,37 @@
/*
* 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 org.jetbrains.plugins.groovy.lang.psi.impl;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiLiteral;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Max Medvedev
*/
public class GrAnnotationUtil {
@Nullable
public static String inferStringAttribute(@NotNull PsiAnnotation annotation, @NotNull String attributeName) {
final PsiAnnotationMemberValue targetValue = annotation.findAttributeValue(attributeName);
if (targetValue instanceof PsiLiteral) {
final Object value = ((PsiLiteral)targetValue).getValue();
if (value instanceof String) return (String)value;
}
return null;
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
@@ -106,7 +107,7 @@ public class GrDelegatesToUtil {
else if (value == null ||
value instanceof PsiLiteralExpression && ((PsiLiteralExpression)value).getType() == PsiType.NULL ||
value instanceof GrLiteral && ((GrLiteral)value).getType() == PsiType.NULL) {
String target = inferStringAttribute(delegatesTo, "target");
String target = GrAnnotationUtil.inferStringAttribute(delegatesTo, "target");
if (target == null) return null;
final int parameter = findTargetParameter(delegatesTo, target);
@@ -132,7 +133,7 @@ public class GrDelegatesToUtil {
final PsiAnnotation targetAnnotation = modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_LANG_DELEGATES_TO_TARGET);
if (targetAnnotation == null) continue;
final String value = inferStringAttribute(targetAnnotation, "value");
final String value = GrAnnotationUtil.inferStringAttribute(targetAnnotation, "value");
if (value == null) continue;
if (value.equals(target)) return i;
@@ -141,17 +142,6 @@ public class GrDelegatesToUtil {
return -1;
}
@Nullable
private static String inferStringAttribute(@NotNull PsiAnnotation annotation, @NotNull String attributeName) {
final PsiAnnotationMemberValue targetValue = annotation.findAttributeValue(attributeName);
if (targetValue instanceof PsiLiteral) {
final Object value = ((PsiLiteral)targetValue).getValue();
if (value instanceof String) return (String)value;
}
return null;
}
@Nullable
private static GrExpression inferCallQualifier(@NotNull GrMethodCall call) {
final GrExpression expression = call.getInvokedExpression();
@@ -225,4 +225,34 @@ class I{
}
''', GrMethodMayBeStaticInspection)
}
void testDelegatesTo() {
testHighlighting('''
def with1(@DelegatesTo.Target() Object target, @DelegatesTo() Closure arg) { //unused
arg.delegate = target
arg()
}
def with2(@<warning descr="@Target is unused">DelegatesTo.Target</warning>('abc') Object target, @DelegatesTo() Closure arg) { //unused
arg.delegate = target
arg()
}
def with3(@DelegatesTo.Target('abc') Object target, @DelegatesTo(target='abc') Closure arg) { //unused
arg.delegate = target
arg()
}
def with4(@<warning descr="@Target is unused">DelegatesTo.Target</warning>('abcd') Object target, @DelegatesTo(target=<warning descr="Target 'abc' does not exist">'abc'</warning>) Closure arg) { //unused
arg.delegate = target
arg()
}
def with5(@<warning descr="@Target is unused">DelegatesTo.Target</warning>() Object target, @DelegatesTo(target=<warning descr="Target 'abc' does not exist">'abc'</warning>) Closure arg) { //unused
arg.delegate = target
arg()
}
''', DelegatesToInspection)
}
}