'java-style property access' inspection

This commit is contained in:
Max Medvedev
2012-10-14 20:34:48 +03:00
parent f8fc1d8304
commit 819555a12e
7 changed files with 260 additions and 144 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports properties accessed via method calls.
</body>
</html>
+4
View File
@@ -637,6 +637,10 @@
<localInspection language="Groovy" groupPath="Groovy" shortName="GroovyTrivialIf" displayName="Redundant 'if' statement"
groupName="Control Flow" enabledByDefault="true"
level="WARNING" implementationClass="org.jetbrains.plugins.groovy.codeInspection.control.GroovyTrivialIfInspection"/>
<localInspection language="Groovy" groupPath="Groovy" shortName="JavaStylePropertiesInvocation"
displayName="Java-style property access"
groupName="Style" enabledByDefault="false" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.codeInspection.style.JavaStylePropertiesInvocationInspection"/>
<localInspection language="Groovy" groupPath="Groovy" shortName="GroovyAccessToStaticFieldLockedOnInstance"
displayName="Access to static field locked on instance data"
groupName="Threading issues" enabledByDefault="true" level="WARNING"
@@ -99,3 +99,4 @@ highlight.if.groovy.object.methods.overridden=Highlight references from class ov
highlight.if.missing.methods.declared=Highlight references from class declaring missingMethod() or missingProperty()
gr.package.inspection.check.scripts=Check scripts
gr.package=Package mismatch
java.style.property.access=Java-style accessor invocation
@@ -0,0 +1,32 @@
package org.jetbrains.plugins.groovy.codeInspection.style;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.utils.JavaStylePropertiesUtil;
import org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
public class JavaStylePropertiesInvocationFixer implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return GroovyIntentionsBundle.message("java.style.properties.invocation.intention.name");
}
@NotNull
@Override
public String getFamilyName() {
return GroovyIntentionsBundle.message("java.style.properties.invocation.intention.family.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement pparent = descriptor.getPsiElement().getParent().getParent();
if (pparent instanceof GrMethodCall){
JavaStylePropertiesUtil.fixJavaStyleProperty((GrMethodCall)pparent);
}
}
}
@@ -0,0 +1,46 @@
package org.jetbrains.plugins.groovy.codeInspection.style;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.psi.PsiElement;
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.codeInspection.utils.JavaStylePropertiesUtil;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
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.path.GrMethodCallExpression;
public class JavaStylePropertiesInvocationInspection extends BaseInspection {
@Override
protected BaseInspectionVisitor buildVisitor() {
return new BaseInspectionVisitor() {
@Override
public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) {
super.visitMethodCallExpression(methodCallExpression);
visitMethodCall(methodCallExpression);
}
@Override
public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
super.visitApplicationStatement(applicationStatement);
visitMethodCall(applicationStatement);
}
private void visitMethodCall(GrMethodCall methodCall) {
if (JavaStylePropertiesUtil.isPropertyAccessor(methodCall)) {
final String message = GroovyInspectionBundle.message("java.style.property.access");
final GrExpression expression = methodCall.getInvokedExpression();
if (expression instanceof GrReferenceExpression) {
PsiElement referenceNameElement = ((GrReferenceExpression)expression).getReferenceNameElement();
registerError(referenceNameElement, message, myFixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
}
}
};
}
private static final LocalQuickFix[] myFixes = new LocalQuickFix[]{new JavaStylePropertiesInvocationFixer()};
}
@@ -0,0 +1,162 @@
/*
* Copyright 2000-2012 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.utils;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils.*;
/**
* @author Max Medvedev
*/
public class JavaStylePropertiesUtil {
private static final Logger LOG = Logger.getInstance(JavaStylePropertiesUtil.class);
public static void fixJavaStyleProperty(GrMethodCall call) {
GrExpression invoked = call.getInvokedExpression();
String accessorName = ((GrReferenceExpression)invoked).getName();
if (isGetterInvocation(call) && invoked instanceof GrReferenceExpression) {
final GrExpression newCall = genRefForGetter(call, accessorName);
call.replaceWithExpression(newCall, true);
}
else if (isSetterInvocation(call) && invoked instanceof GrReferenceExpression) {
final GrStatement newCall = genRefForSetter(call, accessorName);
call.replaceWithStatement(newCall);
}
}
public static boolean isPropertyAccessor(GrMethodCall call) {
return !isInvokedOnMap(call) && (isGetterInvocation(call) || isSetterInvocation(call));
}
private static GrAssignmentExpression genRefForSetter(GrMethodCall call, String accessorName) {
String name = getPropertyNameBySetterName(accessorName);
GrExpression value = call.getExpressionArguments()[0];
GrReferenceExpression refExpr = (GrReferenceExpression)call.getInvokedExpression();
String oldNameStr = refExpr.getReferenceNameElement().getText();
String newRefExpr = StringUtil.trimEnd(refExpr.getText(), oldNameStr) + name;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(call.getProject());
return (GrAssignmentExpression)factory.createStatementFromText(newRefExpr + " = " + value.getText(), call);
}
private static GrExpression genRefForGetter(GrMethodCall call, String accessorName) {
String name = getPropertyNameByGetterName(accessorName, true);
GrReferenceExpression refExpr = (GrReferenceExpression)call.getInvokedExpression();
String oldNameStr = refExpr.getReferenceNameElement().getText();
String newRefExpr = StringUtil.trimEnd(refExpr.getText(), oldNameStr) + name;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(call.getProject());
return factory.createExpressionFromText(newRefExpr, call);
}
private static boolean isInvokedOnMap(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
return expr instanceof GrReferenceExpression && ResolveUtil.isKeyOfMap((GrReferenceExpression)expr);
}
private static boolean isSetterInvocation(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
if (!(expr instanceof GrReferenceExpression)) return false;
GrReferenceExpression refExpr = (GrReferenceExpression)expr;
PsiMethod method;
if (call instanceof GrApplicationStatement) {
PsiElement element = refExpr.resolve();
if (!(element instanceof PsiMethod) || !isSimplePropertySetter(((PsiMethod)element))) return false;
method = (PsiMethod)element;
}
else {
method = call.resolveMethod();
if (!isSimplePropertySetter(method)) return false;
LOG.assertTrue(method != null);
}
if (!GroovyNamesUtil.isValidReference(getPropertyNameBySetterName(method.getName()),
((GrReferenceExpression)expr).getQualifier() != null,
call.getProject())) {
return false;
}
GrArgumentList args = call.getArgumentList();
if (args == null || args.getExpressionArguments().length != 1 || args.getNamedArguments().length > 0) {
return false;
}
GrAssignmentExpression assignment = genRefForSetter(call, refExpr.getName());
GrExpression value = assignment.getLValue();
if (value instanceof GrReferenceExpression &&
call.getManager().areElementsEquivalent(((GrReferenceExpression)value).resolve(), method)) {
return true;
}
return false;
}
private static boolean isGetterInvocation(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
if (!(expr instanceof GrReferenceExpression)) return false;
PsiMethod method = call.resolveMethod();
if (!isSimplePropertyGetter(method)) return false;
LOG.assertTrue(method != null);
if (!GroovyNamesUtil.isValidReference(getPropertyNameByGetterName(method.getName(), true),
((GrReferenceExpression)expr).getQualifier() != null,
call.getProject())) {
return false;
}
GrArgumentList args = call.getArgumentList();
if (args == null || args.getAllArguments().length != 0) {
return false;
}
GrExpression ref = genRefForGetter(call, ((GrReferenceExpression)expr).getName());
if (ref instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)ref).resolve();
PsiManager manager = call.getManager();
if (manager.areElementsEquivalent(resolved, method) || areEquivalentAccessors(method, resolved, manager)) {
return true;
}
}
return false;
}
private static boolean areEquivalentAccessors(PsiMethod method, PsiElement resolved, PsiManager manager) {
if (!(resolved instanceof GrAccessorMethod) || !(method instanceof GrAccessorMethod)) {
return false;
}
if (((GrAccessorMethod)resolved).isSetter() != ((GrAccessorMethod)method).isSetter()) return false;
GrField p1 = ((GrAccessorMethod)resolved).getProperty();
GrField p2 = ((GrAccessorMethod)method).getProperty();
return manager.areElementsEquivalent(p1, p2);
}
}
@@ -16,174 +16,40 @@
package org.jetbrains.plugins.groovy.intentions.style;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.intentions.base.Intention;
import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils.*;
import static org.jetbrains.plugins.groovy.codeInspection.utils.JavaStylePropertiesUtil.fixJavaStyleProperty;
import static org.jetbrains.plugins.groovy.codeInspection.utils.JavaStylePropertiesUtil.isPropertyAccessor;
/**
* @author ilyas
*/
public class JavaStylePropertiesInvocationIntention extends Intention {
private static final Logger LOG = Logger.getInstance(JavaStylePropertiesInvocationIntention.class);
@Override
protected boolean isStopElement(PsiElement element) {
return super.isStopElement(element) || element instanceof GrClosableBlock;
return element instanceof GrClosableBlock || super.isStopElement(element);
}
protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException {
assert element instanceof GrMethodCall;
GrMethodCall call = ((GrMethodCall)element);
GrExpression invoked = call.getInvokedExpression();
String accessorName = ((GrReferenceExpression)invoked).getName();
if (isGetterInvocation(call) && invoked instanceof GrReferenceExpression) {
final GrExpression newCall = genRefForGetter(call, accessorName);
call.replaceWithExpression(newCall, true);
if (element instanceof GrMethodCall) {
fixJavaStyleProperty(((GrMethodCall)element));
}
else if (isSetterInvocation(call) && invoked instanceof GrReferenceExpression) {
final GrStatement newCall = genRefForSetter(call, accessorName);
call.replaceWithStatement(newCall);
}
}
private static GrAssignmentExpression genRefForSetter(GrMethodCall call, String accessorName) {
String name = getPropertyNameBySetterName(accessorName);
GrExpression value = call.getExpressionArguments()[0];
GrReferenceExpression refExpr = (GrReferenceExpression)call.getInvokedExpression();
String oldNameStr = refExpr.getReferenceNameElement().getText();
String newRefExpr = StringUtil.trimEnd(refExpr.getText(), oldNameStr) + name;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(call.getProject());
return (GrAssignmentExpression)factory.createStatementFromText(newRefExpr + " = " + value.getText(), call);
}
private static GrExpression genRefForGetter(GrMethodCall call, String accessorName) {
String name = getPropertyNameByGetterName(accessorName, true);
GrReferenceExpression refExpr = (GrReferenceExpression)call.getInvokedExpression();
String oldNameStr = refExpr.getReferenceNameElement().getText();
String newRefExpr = StringUtil.trimEnd(refExpr.getText(), oldNameStr) + name;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(call.getProject());
return factory.createExpressionFromText(newRefExpr, call);
}
@NotNull
protected PsiElementPredicate getElementPredicate() {
return new JavaPropertyInvocationPredicate();
}
public static boolean isPropertyAccessor(GrMethodCall call) {
return !isInvokedOnMap(call) && (isGetterInvocation(call) || isSetterInvocation(call));
}
private static boolean isInvokedOnMap(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
return expr instanceof GrReferenceExpression && ResolveUtil.isKeyOfMap((GrReferenceExpression)expr);
}
private static boolean isSetterInvocation(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
if (!(expr instanceof GrReferenceExpression)) return false;
GrReferenceExpression refExpr = (GrReferenceExpression)expr;
PsiMethod method;
if (call instanceof GrApplicationStatement) {
PsiElement element = refExpr.resolve();
if (!(element instanceof PsiMethod) || !isSimplePropertySetter(((PsiMethod)element))) return false;
method = (PsiMethod)element;
}
else {
method = call.resolveMethod();
if (!isSimplePropertySetter(method)) return false;
LOG.assertTrue(method != null);
}
if (!GroovyNamesUtil.isValidReference(getPropertyNameBySetterName(method.getName()),
((GrReferenceExpression)expr).getQualifier() != null,
call.getProject())) {
return false;
}
GrArgumentList args = call.getArgumentList();
if (args == null || args.getExpressionArguments().length != 1 || args.getNamedArguments().length > 0) {
return false;
}
GrAssignmentExpression assignment = genRefForSetter(call, refExpr.getName());
GrExpression value = assignment.getLValue();
if (value instanceof GrReferenceExpression &&
call.getManager().areElementsEquivalent(((GrReferenceExpression)value).resolve(), method)) {
return true;
}
return false;
}
private static boolean isGetterInvocation(GrMethodCall call) {
GrExpression expr = call.getInvokedExpression();
if (!(expr instanceof GrReferenceExpression)) return false;
PsiMethod method = call.resolveMethod();
if (!isSimplePropertyGetter(method)) return false;
LOG.assertTrue(method != null);
if (!GroovyNamesUtil.isValidReference(getPropertyNameByGetterName(method.getName(), true),
((GrReferenceExpression)expr).getQualifier() != null,
call.getProject())) {
return false;
}
GrArgumentList args = call.getArgumentList();
if (args == null || args.getAllArguments().length != 0) {
return false;
}
GrExpression ref = genRefForGetter(call, ((GrReferenceExpression)expr).getName());
if (ref instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)ref).resolve();
PsiManager manager = call.getManager();
if (manager.areElementsEquivalent(resolved, method) || areEquivalentAccessors(method, resolved, manager)) {
return true;
return new PsiElementPredicate() {
public boolean satisfiedBy(PsiElement element) {
return element instanceof GrMethodCall && isPropertyAccessor((GrMethodCall)element);
}
}
return false;
}
private static boolean areEquivalentAccessors(PsiMethod method, PsiElement resolved, PsiManager manager) {
if (!(resolved instanceof GrAccessorMethod) || !(method instanceof GrAccessorMethod)) {
return false;
}
if (((GrAccessorMethod)resolved).isSetter() != ((GrAccessorMethod)method).isSetter()) return false;
GrField p1 = ((GrAccessorMethod)resolved).getProperty();
GrField p2 = ((GrAccessorMethod)method).getProperty();
return manager.areElementsEquivalent(p1, p2);
}
private static class JavaPropertyInvocationPredicate implements PsiElementPredicate {
public boolean satisfiedBy(PsiElement element) {
if (!(element instanceof GrMethodCall)) return false;
return isPropertyAccessor((GrMethodCall)element);
}
};
}
}