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

More easily detect overridden methods & calls to super #1040

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
package com.tngtech.archunit.core.domain;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;

Expand Down Expand Up @@ -125,6 +123,46 @@ public String getDescription() {
return "Method <" + getFullName() + ">";
}

@PublicAPI(usage = ACCESS) public boolean isOverridden() {
JavaClass superClass;
for (Optional<JavaType> superClassOpt =
getOwner().getSuperclass(); superClassOpt.isPresent();
superClassOpt = superClass.getSuperclass()) {
JavaParameterizedType superClassType = (JavaParameterizedType) superClassOpt.get();
superClass = superClassType.toErasure();
List<JavaTypeVariable<JavaClass>> superClassTypeParameters =
superClass.getTypeParameters();
Map<JavaType/*Variable<JavaClass>*/, JavaType> typeParametersToOverridenTypes =
new HashMap<>();
for (int i = 0; i < superClassTypeParameters.size(); i++) {
typeParametersToOverridenTypes.put(superClassTypeParameters.get(i),
superClassType.getActualTypeArguments().get(i));
}

for (JavaMethod superMethod : superClass.getAllMethods()) {
if (!superMethod.getName().equals(getName())) {
return false;
}

List<JavaParameter> parameterTypes = getParameters();
List<JavaParameter> superMethodParameterTypes = superMethod.getParameters();
if (parameterTypes.size() != superMethodParameterTypes.size()) {
return false;
}
for (int i = 0; i < parameterTypes.size(); i++) {
JavaParameter parameter = parameterTypes.get(i);
JavaParameter superParameter = superMethodParameterTypes.get(i);
if (!parameter.equals(superParameter) && !typeParametersToOverridenTypes.get(
superParameter.getType()).equals(parameter.getType())) {
return false;
}
}
return true;
}
}
return false;
}

@ResolvesTypesViaReflection
@MayResolveTypesViaReflection(reason = "Just part of a bigger resolution process")
private class ReflectMethodSupplier implements Supplier<Method> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.tngtech.archunit.core.domain;


import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.Test;

import java.util.stream.Collectors;

import static com.tngtech.archunit.testutil.Assertions.assertThat;

public class JavaMethodTest {
@Test
public void isOverriddenTest() {
class Base {
void method1() {
}

void method1(int x) {
}
}
class Child extends Base {
void method1() {
}

void method2() {
}
}
class GrandChild extends Child {
void method1() {

}

void method1(int x) {

}

void method2() {

}

void method3() {

}

}
ClassFileImporter importer = new ClassFileImporter();
JavaClass baseClass = importer.importClass(Base.class);
JavaClass childClass = importer.importClass(Child.class);
JavaClass grandChildClass = importer.importClass(GrandChild.class);
assertThat(baseClass.getMethod("method1").isOverridden()).isFalse();
assertThat(baseClass.getMethod("method1", int.class).isOverridden()).isFalse();
assertThat(childClass.getMethod("method1").isOverridden()).isTrue();
assertThat(childClass.getMethod("method2").isOverridden()).isFalse();
assertThat(grandChildClass.getMethod("method1").isOverridden()).isTrue();
assertThat(grandChildClass.getMethod("method1", int.class).isOverridden()).isTrue();
assertThat(grandChildClass.getMethod("method2").isOverridden()).isTrue();
assertThat(grandChildClass.getMethod("method3").isOverridden()).isFalse();
//TODO add testing for methods with generic parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we indeed don't recognize all overriden generic methods:

    @Test
    public void overridden_generic_methods_are_supported() {
        class Parent<T extends Number> {
            void method(T t) { }
        }
        class Child extends Parent<Integer> {
            @Override
            void method(Integer t) { }
        }
        JavaClass childClass = new ClassFileImporter().importClass(Child.class);
        JavaMethod method = childClass.getMethod("method", Integer.class);
        assertThat(method.isOverridden()).isTrue();  // Expecting value to be true but was false 
    }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, this might complicate the code now.
I'll see what I can do.

}
@Test
public void overridden_generic_methods_are_supported() {
class Parent<T extends Number> {
void method(T t) { }
}
class Child extends Parent<Integer> {
@Override
void method(Integer t) { }

}
ClassFileImporter classFileImporter = new ClassFileImporter();
JavaClass childClass = classFileImporter.importClass(Child.class);
JavaMethod method = childClass.getMethod("method", Integer.class);
assertThat(method.isOverridden()).isTrue();
}
}