improved searching of method duplicates

This commit is contained in:
Maxim Medvedev
2010-04-23 10:32:41 +04:00
parent 184632cb63
commit 9a5770b612
12 changed files with 124 additions and 62 deletions
@@ -335,3 +335,4 @@ property.name.expected=Property name expected
add.method.body=Add method body
wildcards.are.not.allowed.in.extends.list=A super type may not specify a wildcard type
method.doesnot.override.super=Method does not override method from its superclass
method.duplicate={0} is already defined
@@ -33,13 +33,13 @@ import com.intellij.psi.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
@@ -49,7 +49,6 @@ import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicProperty
import org.jetbrains.plugins.groovy.codeInspection.GroovyImportsTracker;
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
import org.jetbrains.plugins.groovy.highlighter.DefaultHighlighter;
import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.*;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
@@ -83,6 +82,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
@@ -953,34 +953,33 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
}
private static void checkDuplicateMethod(GrMethod[] methods, AnnotationHolder holder) {
final Map<GrMethod, List<GrMethod>> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy<GrMethod>() {
public int computeHashCode(GrMethod method) {
return method.getSignature(PsiSubstitutor.EMPTY).hashCode();
}
public boolean equals(GrMethod method1, GrMethod method2) {
return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
}
});
Map<MethodSignature, List<GrMethod>> map = GrClosureSignatureUtil.findMethodSignatures(methods);
processMethodDuplicates(map, holder);
}
protected static void processMethodDuplicates(Map<GrMethod, List<GrMethod>> map, AnnotationHolder holder) {
HashSet<GrMethod> duplicateMethodsWarning = new HashSet<GrMethod>();
HashSet<GrMethod> duplicateMethodsErrors = new HashSet<GrMethod>();
DuplicatesUtil.collectMethodDuplicates(map, duplicateMethodsWarning, duplicateMethodsErrors);
for (GrMethod duplicateMethod : duplicateMethodsErrors) {
holder.createErrorAnnotation(duplicateMethod.getNameIdentifierGroovy(),
GroovyBundle.message("repetitive.method.name.signature.and.return.type"));
}
for (GrMethod duplicateMethod : duplicateMethodsWarning) {
holder.createWarningAnnotation(duplicateMethod.getNameIdentifierGroovy(), GroovyBundle.message("repetitive.method.name.signature"));
protected static void processMethodDuplicates(Map<MethodSignature, List<GrMethod>> map, AnnotationHolder holder) {
for (MethodSignature signature : map.keySet()) {
List<GrMethod> methods = map.get(signature);
if (methods.size() > 1) {
String signaturePresentation = getSignaturePresentation(signature);
for (GrMethod method : methods) {
holder.createErrorAnnotation(method.getNameIdentifierGroovy(), GroovyBundle.message("method.duplicate", signaturePresentation));
}
}
}
}
private static String getSignaturePresentation(MethodSignature signature) {
StringBuilder builder = new StringBuilder();
builder.append(signature.getName()).append('(');
PsiType[] types = signature.getParameterTypes();
for (PsiType type : types) {
builder.append(type.getPresentableText()).append(", ");
}
if (types.length > 0) builder.delete(builder.length() - 2, builder.length());
builder.append(")");
return builder.toString();
}
private static void checkTypeDefinition(AnnotationHolder holder, GrTypeDefinition typeDefinition) {
final GroovyConfigUtils configUtils = GroovyConfigUtils.getInstance();
@@ -328,18 +328,16 @@ public class GroovyParameterInfoHandler implements ParameterInfoHandler<GroovyPs
if (parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
if (i > 0) buffer.append(", ");
final String name = parameters[i].getName();
final PsiType psiType = parameters[i].getType();
if (name == null) {
buffer.append(psiType == null ? "null" : psiType.getPresentableText());
if (psiType == null) {
buffer.append("def");
}
else {
String typeText = psiType == null ? "def" : psiType.getPresentableText();
buffer.append(typeText).append(' ').append(name);
final GrExpression initializer = parameters[i].getDefaultInitializer();
if (initializer != null) {
buffer.append(" = ").append(initializer.getText());
}
buffer.append(psiType.getPresentableText());
}
final GrExpression initializer = parameters[i].getDefaultInitializer();
if (initializer != null) {
buffer.append(" = ").append(initializer.getText());
}
}
}
@@ -35,7 +35,4 @@ public interface GrClosureParameter {
GrExpression getDefaultInitializer();
boolean isValid();
@Nullable
String getName();
}
@@ -15,6 +15,7 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.api.types;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,6 +24,8 @@ import org.jetbrains.annotations.Nullable;
* @author Maxim.Medvedev
*/
public interface GrClosureSignature {
@NotNull PsiSubstitutor getSubstitutor();
@NotNull
GrClosureParameter[] getParameters();
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
/**
* @author ven
@@ -29,12 +29,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
*/
public class GrClosureParameterImpl implements GrClosureParameter {
@Nullable final PsiType myType;
@Nullable final String myName=null;
final boolean myOptional;
@Nullable final GrExpression myDefaultInitializer;
public GrClosureParameterImpl(/*String name,*/ PsiType type, boolean optional, GrExpression defaultInitializer) {
// myName = name;
public GrClosureParameterImpl(PsiType type, boolean optional, GrExpression defaultInitializer) {
myType = type;
myOptional = optional;
if (myOptional) {
@@ -45,16 +43,12 @@ public class GrClosureParameterImpl implements GrClosureParameter {
}
}
public GrClosureParameterImpl(@Nullable PsiType type) {
this(/*null,*/ type, false, null);
}
public GrClosureParameterImpl(PsiParameter parameter) {
this(parameter, PsiSubstitutor.EMPTY);
}
public GrClosureParameterImpl(PsiParameter parameter, PsiSubstitutor substitutor) {
this(/*parameter.getName(), */substitutor.substitute(parameter.getType()),
this(substitutor.substitute(parameter.getType()),
parameter instanceof GrParameter ? ((GrParameter)parameter).isOptional() : false,
parameter instanceof GrParameter ? ((GrParameter)parameter).getDefaultInitializer() : null);
}
@@ -78,11 +72,6 @@ public class GrClosureParameterImpl implements GrClosureParameter {
return (myType == null || myType.isValid()) && (myDefaultInitializer == null || myDefaultInitializer.isValid());
}
@Nullable
public String getName() {
return myName;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GrClosureParameter) {
@@ -32,6 +32,7 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
private final boolean myIsVarargs;
@Nullable private final PsiType myReturnType;
@NotNull private final GrClosureParameter[] myParameters;
@NotNull private PsiSubstitutor mySubstitutor;
public GrClosureSignatureImpl(@NotNull PsiParameter[] parameters, @Nullable PsiType returnType, @NotNull PsiSubstitutor substitutor) {
myReturnType = substitutor.substitute(returnType);
@@ -46,16 +47,13 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
else {
myIsVarargs = false;
}
mySubstitutor = substitutor;
}
public GrClosureSignatureImpl(PsiParameter[] parameters, PsiType returnType) {
this(parameters, returnType, PsiSubstitutor.EMPTY);
}
public GrClosureSignatureImpl(PsiParameter[] parameters) {
this(parameters, null);
}
public GrClosureSignatureImpl(@NotNull GrClosableBlock block) {
this(block.getAllParameters(), block.getReturnType());
}
@@ -68,7 +66,7 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
this(method.getParameterList().getParameters(), PsiUtil.getSmartReturnType(method), substitutor);
}
private GrClosureSignatureImpl(@NotNull GrClosureParameter[] params, @Nullable PsiType returnType, boolean isVarArgs) {
GrClosureSignatureImpl(@NotNull GrClosureParameter[] params, @Nullable PsiType returnType, boolean isVarArgs) {
myParameters = params;
myReturnType = returnType;
myIsVarargs = isVarArgs;
@@ -85,6 +83,11 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
return myReturnType;
}
@NotNull
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
@NotNull
public GrClosureParameter[] getParameters() {
GrClosureParameter[] result = new GrClosureParameter[myParameters.length];
@@ -13,25 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.lang.psi.impl;
package org.jetbrains.plugins.groovy.lang.psi.impl.types;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
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.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureImpl;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
/**
* @author Maxim.Medvedev
@@ -70,7 +72,7 @@ public class GrClosureSignatureUtil {
}
private static boolean isApplicable(GrClosureSignature signature, PsiType[] args, PsiManager manager, GlobalSearchScope scope) {
GrClosureParameter[] params = signature.getParameters();
GrClosureParameter[] params = signature.getParameters();
if (args.length > params.length && !signature.isVarargs()) return false;
int optional = getOptionalParamCount(signature, false);
int notOptional = params.length - optional;
@@ -344,4 +346,62 @@ public class GrClosureSignatureUtil {
return copy;
}
}
public static List<MethodSignature> generateAllSignaturesForMethod(GrMethod method, PsiSubstitutor substitutor) {
GrClosureSignature signature = createSignature(method, substitutor);
String name = method.getName();
GrClosureParameter[] params = signature.getParameters();
PsiTypeParameter[] typeParameters = method.getTypeParameters();
ArrayList<PsiType> newParams = new ArrayList<PsiType>(params.length);
ArrayList<GrClosureParameter> opts = new ArrayList<GrClosureParameter>(params.length);
ArrayList<Integer> optInds = new ArrayList<Integer>(params.length);
for (int i = 0; i < params.length; i++) {
if (params[i].isOptional()) {
opts.add(params[i]);
optInds.add(i);
}
else {
newParams.add(params[i].getType());
}
}
List<MethodSignature> result = new ArrayList<MethodSignature>(opts.size() + 1);
result.add(generateSignature(name, newParams, typeParameters, substitutor));
for (int i = 0; i < opts.size(); i++) {
newParams.add(optInds.get(i), opts.get(i).getType());
result.add(generateSignature(name, newParams, typeParameters, substitutor));
}
return result;
}
public static Map<MethodSignature, List<GrMethod>> findMethodSignatures(GrMethod[] methods) {
List<Pair<MethodSignature, GrMethod>> signatures = new ArrayList<Pair<MethodSignature, GrMethod>>();
for (GrMethod method : methods) {
List<MethodSignature> current = generateAllSignaturesForMethod(method, PsiSubstitutor.EMPTY);
for (MethodSignature signature : current) {
signatures.add(new Pair<MethodSignature, GrMethod>(signature, method));
}
}
THashMap<MethodSignature, List<GrMethod>> map = new THashMap<MethodSignature, List<GrMethod>>();
for (Pair<MethodSignature, GrMethod> pair : signatures) {
List<GrMethod> list = map.get(pair.first);
if (list == null) {
list = new ArrayList<GrMethod>();
map.put(pair.first, list);
}
list.add(pair.second);
}
return map;
}
private static MethodSignature generateSignature(String name,
List<PsiType> paramTypes,
PsiTypeParameter[] typeParameters,
PsiSubstitutor substitutor) {
return MethodSignatureUtil.createMethodSignature(name, paramTypes.toArray(new PsiType[paramTypes.size()]), typeParameters, substitutor);
}
}
@@ -64,11 +64,11 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.JavaIdentifier;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor;
@@ -238,4 +238,6 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase {
public void testOverrideAnnotation() throws Exception {doTest();}
public void testClosureCallWithTupleTypeArgument() throws Exception {doTest();}
public void testMethodDuplicates() throws Exception {doTest();}
}
@@ -0,0 +1,9 @@
def <error descr="foo(String, int) already defined">foo</error>(String s = "a", int i, double y = 4) {
}
def foo(int i, double y) {}
def <error descr="foo(String, int) already defined">foo</error>(String s, int i) {}
def foo(String s, double y){}