Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid ClassCastException in RedundantAbstractMethodCheck #2663

Merged
merged 2 commits into from Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -19,20 +19,20 @@
*/
package org.sonar.java.checks;

import com.google.common.collect.ImmutableMultiset;
import org.sonar.check.Rule;
import org.sonar.java.resolve.JavaSymbol;
import org.sonar.java.model.JUtils;
import org.sonar.java.resolve.MethodJavaType;
import org.sonar.java.resolve.ParametrizedTypeJavaType;
import org.sonar.java.resolve.SymbolMetadataResolve;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
import org.sonar.plugins.java.api.semantic.SymbolMetadata.AnnotationInstance;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Tree;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;

@Rule(key = "S3038")
Expand All @@ -50,51 +50,55 @@ public void visitNode(Tree tree) {
}
Symbol.MethodSymbol method = ((MethodTree) tree).symbol();
if (method.isAbstract() && method.owner().isAbstract()) {
checkMethod((JavaSymbol.MethodJavaSymbol) method);
checkMethod(method);
}
}

private void checkMethod(JavaSymbol.MethodJavaSymbol method) {
JavaSymbol.MethodJavaSymbol overridee = method.overriddenSymbol();
private void checkMethod(Symbol.MethodSymbol method) {
Symbol.MethodSymbol overridee = method.overriddenSymbol();
if (overridee != null && overridee.owner().isInterface() && !differentContract(method, overridee)) {
reportIssue(method.declaration(), "\"" + method.name() + "\" is defined in the \"" + overridee.owner().name() + "\" interface and can be removed from this class.");
}
}

private static boolean differentContract(JavaSymbol.MethodJavaSymbol method, JavaSymbol.MethodJavaSymbol overridee) {
private static boolean differentContract(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
return removingParametrizedAspect(method, overridee)
|| differentThrows(method, overridee)
|| differentReturnType(method, overridee)
|| differentParameters(method, overridee)
|| differentAnnotations(method.metadata(), overridee.metadata());
}

private static boolean removingParametrizedAspect(JavaSymbol.MethodJavaSymbol method, JavaSymbol.MethodJavaSymbol overridee) {
return !method.isParametrized() && overridee.isParametrized();
private static boolean removingParametrizedAspect(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
return !JUtils.isParametrizedMethod(method) && JUtils.isParametrizedMethod(overridee);
}

private static boolean differentThrows(JavaSymbol.MethodJavaSymbol method, JavaSymbol.MethodJavaSymbol overridee) {
return !ImmutableMultiset.copyOf(method.thrownTypes()).equals(ImmutableMultiset.copyOf(overridee.thrownTypes()));
private static boolean differentThrows(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
return !(new HashSet<>(method.thrownTypes()).equals(new HashSet<>(overridee.thrownTypes())));
}

private static boolean differentReturnType(JavaSymbol.MethodJavaSymbol method, JavaSymbol.MethodJavaSymbol overridee) {
private static boolean differentReturnType(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
Type methodResultType = resultType(method);
Type overrideeResultType = resultType(overridee);
return specializationOfReturnType(methodResultType.erasure(), overrideeResultType.erasure()) || useRawTypeOfParametrizedType(methodResultType, overrideeResultType);
}

private static Type resultType(JavaSymbol.MethodJavaSymbol method) {
return ((MethodJavaType) method.type()).resultType();
private static Type resultType(Symbol.MethodSymbol method) {
Type type = method.type();
if (type instanceof MethodJavaType) {
return ((MethodJavaType) type).resultType();
}
return method.returnType().type();
}

private static boolean specializationOfReturnType(Type methodResultType, Type overrideeResultType) {
return !methodResultType.isVoid()
&& (methodResultType.isSubtypeOf(overrideeResultType) && !overrideeResultType.isSubtypeOf(methodResultType));
}

private static boolean differentParameters(JavaSymbol.MethodJavaSymbol method, JavaSymbol.MethodJavaSymbol overridee) {
private static boolean differentParameters(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
return useRawTypeOfParametrizedType(method.parameterTypes(), overridee.parameterTypes())
|| differentAnnotations(method.getParameters().scopeSymbols(), overridee.getParameters().scopeSymbols());
|| differentAnnotationsOnParameters(method, overridee);
}

private static boolean useRawTypeOfParametrizedType(List<Type> methodParamTypes, List<Type> overrideeParamType) {
Expand All @@ -107,19 +111,26 @@ private static boolean useRawTypeOfParametrizedType(List<Type> methodParamTypes,
}

private static boolean useRawTypeOfParametrizedType(Type methodParam, Type overrideeParam) {
return overrideeParam instanceof ParametrizedTypeJavaType && methodParam.equals(overrideeParam.erasure());
if (overrideeParam instanceof ParametrizedTypeJavaType) {
return methodParam.equals(overrideeParam.erasure());
}
return !JUtils.isParametrized(methodParam)
&& JUtils.isParametrized(overrideeParam)
&& methodParam.erasure().equals(overrideeParam.erasure());
}

private static boolean differentAnnotations(List<JavaSymbol> methodParamSymbols, List<JavaSymbol> overrideeParamSymbols) {
for (int i = 0; i < methodParamSymbols.size(); i++) {
if (differentAnnotations(methodParamSymbols.get(i).metadata(), overrideeParamSymbols.get(i).metadata())) {
private static boolean differentAnnotationsOnParameters(Symbol.MethodSymbol method, Symbol.MethodSymbol overridee) {
for (int i = 0; i < method.parameterTypes().size(); i++) {
if (differentAnnotations(
JUtils.parameterAnnotations(method, i),
JUtils.parameterAnnotations(overridee, i))) {
return true;
}
}
return false;
}

private static boolean differentAnnotations(SymbolMetadataResolve methodMetadata, SymbolMetadataResolve overrideeMetadata) {
private static boolean differentAnnotations(SymbolMetadata methodMetadata, SymbolMetadata overrideeMetadata) {
for (AnnotationInstance annotation : methodMetadata.annotations()) {
Type type = annotation.symbol().type();
if (!type.is("java.lang.Override") && !overrideeMetadata.isAnnotatedWith(type.fullyQualifiedName())) {
Expand Down
Expand Up @@ -13,7 +13,7 @@ abstract static class B implements I {
}

abstract static class C implements I {
@Override abstract void bar();
abstract void bar();
}

abstract static class D extends C {
Expand Down