Assignability checks in groovy annotator for statically compiled code

This commit is contained in:
Maxim.Medvedev
2012-05-30 16:11:57 +04:00
parent eb52dde764
commit 713683550b
3 changed files with 127 additions and 8 deletions
@@ -60,6 +60,7 @@ import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.annotator.intentions.*;
import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicMethodFix;
import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicPropertyFix;
import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection;
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
import org.jetbrains.plugins.groovy.debugger.fragments.GroovyCodeFragment;
import org.jetbrains.plugins.groovy.dsl.toplevel.AnnotatedContextFilter;
@@ -131,6 +132,9 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
if (element instanceof GroovyPsiElement) {
myHolder = holder;
((GroovyPsiElement)element).accept(this);
if (isCompileStatic(element)) {
GroovyAssignabilityCheckInspection.checkElement((GroovyPsiElement)element, holder);
}
myHolder = null;
}
else {
@@ -413,8 +417,8 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
}
}
private static boolean isCompileStatic(GrReferenceExpression referenceExpression) {
PsiMember containingMember = PsiTreeUtil.getParentOfType(referenceExpression, PsiMember.class);
private static boolean isCompileStatic(PsiElement e) {
PsiMember containingMember = PsiTreeUtil.getParentOfType(e, PsiMember.class);
return containingMember != null && GroovyPsiManager.getInstance(containingMember.getProject()).isCompileStatic(containingMember);
}
@@ -16,14 +16,22 @@
package org.jetbrains.plugins.groovy.codeInspection.assignment;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,7 +39,6 @@ import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.annotator.GroovyAnnotator;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.codeInspection.GroovyFix;
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
@@ -45,6 +52,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
@@ -56,10 +64,12 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.*;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceResolveUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -80,11 +90,6 @@ import java.util.Map;
public class GroovyAssignabilityCheckInspection extends BaseInspection {
private static final Logger LOG = Logger.getInstance(GroovyAssignabilityCheckInspection.class);
@Override
protected GroovyFix buildFix(PsiElement location) {
return super.buildFix(location); //To change body of overridden methods use File | Settings | File Templates.
}
@Nls
@NotNull
@Override
@@ -159,6 +164,8 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
@Override
public void visitMethod(GrMethod method) {
if (GroovyPsiManager.getInstance(method.getProject()).isCompileStatic(method)) return;
super.visitMethod(method);
final GrOpenBlock block = method.getBlock();
if (block == null) return;
@@ -178,6 +185,18 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
});
}
@Override
public void visitField(GrField field) {
if (GroovyPsiManager.getInstance(field.getProject()).isCompileStatic(field)) return;
super.visitField(field);
}
@Override
public void visitTypeDefinition(GrTypeDefinition typeDefinition) {
if (GroovyPsiManager.getInstance(typeDefinition.getProject()).isCompileStatic(typeDefinition)) return;
super.visitTypeDefinition(typeDefinition);
}
@Override
public void visitReturnStatement(GrReturnStatement returnStatement) {
super.visitReturnStatement(returnStatement);
@@ -761,4 +780,75 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
}
return GrReferenceResolveUtil.getQualifierType(place);
}
private static class AnnotatingVisitor extends MyVisitor {
private AnnotationHolder myHolder;
@Override
protected void registerError(@NotNull final PsiElement location,
final String description,
final LocalQuickFix[] fixes,
final ProblemHighlightType highlightType) {
Annotation annotation = myHolder.createErrorAnnotation(location, description);
for (final LocalQuickFix fix : fixes) {
annotation.registerFix(new IntentionAction() {
@NotNull
@Override
public String getText() {
return fix.getName();
}
@NotNull
@Override
public String getFamilyName() {
return fix.getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
InspectionManager manager = InspectionManager.getInstance(project);
ProblemDescriptor descriptor = manager.createProblemDescriptor(location, description, fixes, highlightType, fixes.length == 1, false);
fix.applyFix(project, descriptor);
}
@Override
public boolean startInWriteAction() {
return true;
}
});
}
}
@Override
public void visitElement(GroovyPsiElement element) {
//do nothing
}
}
private static final ThreadLocal<AnnotatingVisitor> visitor = new ThreadLocal<AnnotatingVisitor>() {
@Override
protected AnnotatingVisitor initialValue() {
return new AnnotatingVisitor();
}
};
public static void checkElement(GroovyPsiElement e, AnnotationHolder holder) {
AnnotatingVisitor annotatingVisitor = visitor.get();
AnnotationHolder oldHolder = annotatingVisitor.myHolder;
try {
annotatingVisitor.myHolder = holder;
e.accept(annotatingVisitor);
}
finally {
annotatingVisitor.myHolder = oldHolder;
}
}
}
@@ -1033,4 +1033,29 @@ public @interface CompileStatic {
myFixture.testHighlighting(true, true, true)
}
void testCompileStaticWithAssignabilityCheck() {
myFixture.addClass('''\
package groovy.transform;
public @interface CompileStatic {
}''')
myFixture.configureByText('_.groovy', '''\
import groovy.transform.CompileStatic
class A {
def foo(String s) {
int x = <warning descr="Cannot assign 'Date' to 'int'">new Date()</warning>
}
@CompileStatic
def bar() {
int x = <error descr="Cannot assign 'Date' to 'int'">new Date()</error>
}
}
''')
myFixture.enableInspections(GroovyAssignabilityCheckInspection)
myFixture.checkHighlighting(true, false, true)
}
}