Skip to content

Commit f57bfd3

Browse files
committed
ignore synthetic bridge methods in ProxyRules
resolves #1193 Signed-off-by: Manfred Hanke <[email protected]> Signed-off-by: Peter Gafert <[email protected]>
1 parent e5d7dbc commit f57bfd3

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

archunit/src/main/java/com/tngtech/archunit/library/ProxyRules.java

+17-11
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@
2020
import com.tngtech.archunit.PublicAPI;
2121
import com.tngtech.archunit.base.DescribedPredicate;
2222
import com.tngtech.archunit.core.domain.AccessTarget.MethodCallTarget;
23+
import com.tngtech.archunit.core.domain.JavaAccess.Functions.Get;
2324
import com.tngtech.archunit.core.domain.JavaClass;
24-
import com.tngtech.archunit.core.domain.JavaMethodCall;
2525
import com.tngtech.archunit.lang.ArchCondition;
2626
import com.tngtech.archunit.lang.ArchRule;
2727
import com.tngtech.archunit.lang.ConditionEvents;
2828
import com.tngtech.archunit.lang.SimpleConditionEvent;
2929

3030
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
31+
import static com.tngtech.archunit.base.DescribedPredicate.not;
32+
import static com.tngtech.archunit.core.domain.JavaModifier.BRIDGE;
3133
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
34+
import static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier;
3235
import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
3336
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
3437

@@ -44,7 +47,7 @@ private ProxyRules() {
4447
/**
4548
* Returns a rule that checks that none of the given classes directly calls
4649
* other methods declared in the same class that are annotated with the
47-
* given annotation.
50+
* given annotation (ignoring calls from synthetic bridge methods).
4851
*
4952
* <p>
5053
* As an example, the Spring Framework handles transactions by creating a proxy.
@@ -92,7 +95,8 @@ public static ArchRule no_classes_should_directly_call_other_methods_declared_in
9295

9396
/**
9497
* Returns a rule that checks that none of the given classes directly calls
95-
* other methods declared in the same class that matches the given predicate.
98+
* other methods declared in the same class that matches the given predicate
99+
* (ignoring calls from synthetic bridge methods).
96100
*
97101
* <p>
98102
* For an example, see {@link #no_classes_should_directly_call_other_methods_declared_in_the_same_class_that_are_annotated_with(Class)}
@@ -106,8 +110,8 @@ public static ArchRule no_classes_should_directly_call_other_methods_declared_in
106110

107111
/**
108112
* Returns a condition that matches classes that directly calls other methods
109-
* declared in the same class that are annotated with the given annotation.
110-
*
113+
* declared in the same class that are annotated with the given annotation
114+
* (ignoring calls from synthetic bridge methods).
111115
* <p>
112116
* As an example, the Spring Framework handles transactions by creating a proxy.
113117
* This proxy does the actual transaction management every time a method
@@ -155,8 +159,8 @@ public static ArchCondition<JavaClass> directly_call_other_methods_declared_in_t
155159

156160
/**
157161
* Returns a condition that matches classes that directly calls other methods
158-
* declared in the same class that matches the given predicate.
159-
*
162+
* declared in the same class that matches the given predicate
163+
* (ignoring calls from synthetic bridge methods).
160164
* <p>
161165
* For an example, see {@link #directly_call_other_methods_declared_in_the_same_class_that_are_annotated_with(Class)}
162166
* </p>
@@ -166,10 +170,12 @@ public static ArchCondition<JavaClass> directly_call_other_methods_declared_in_t
166170
return new ArchCondition<JavaClass>("directly call other methods declared in the same class that " + predicate.getDescription()) {
167171
@Override
168172
public void check(JavaClass javaClass, ConditionEvents events) {
169-
for (JavaMethodCall call : javaClass.getMethodCallsFromSelf()) {
170-
boolean satisfied = call.getOriginOwner().equals(call.getTargetOwner()) && predicate.test(call.getTarget());
171-
events.add(new SimpleConditionEvent(call, satisfied, call.getDescription()));
172-
}
173+
javaClass.getMethodCallsFromSelf().stream()
174+
.filter(Get.origin().is(not(modifier(BRIDGE))))
175+
.forEach(call -> {
176+
boolean satisfied = call.getOriginOwner().equals(call.getTargetOwner()) && predicate.test(call.getTarget());
177+
events.add(new SimpleConditionEvent(call, satisfied, call.getDescription()));
178+
});
173179
}
174180
};
175181
}

archunit/src/test/java/com/tngtech/archunit/library/ProxyRulesTest.java

+24
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,30 @@ void selfProxied() {
7373
quoteMethod(SomeClass.class, "evil"), quoteMethod(SomeClass.class, "selfProxied")));
7474
}
7575

76+
@Test
77+
@UseDataProvider("call_own_method_with_specific_annotation_rules")
78+
public void ignores_synthetic_bridge_method_calling_other_method_declared_in_the_same_class(ArchRule rule, String expectedDescription) {
79+
abstract class GenericBaseClass<T> {
80+
@SuppressWarnings("unused")
81+
abstract void selfProxied(T value);
82+
}
83+
84+
class SpecificChild extends GenericBaseClass<String> {
85+
@Override
86+
@ProxyAnnotation
87+
void selfProxied(String value) {
88+
}
89+
90+
// The compiler generates a synthetic bridge method `void selfProxied(java.lang.Object)`
91+
// that delegates to `void selfProxied(String value)`.
92+
}
93+
94+
assertThatRule(rule)
95+
.hasDescriptionContaining(expectedDescription)
96+
.checking(new ClassFileImporter().importClasses(GenericBaseClass.class, SpecificChild.class))
97+
.hasNoViolation();
98+
}
99+
76100
private String quoteMethod(Class<?> owner, String methodName) {
77101
return quote(owner.getSimpleName() + "." + methodName + "()");
78102
}

0 commit comments

Comments
 (0)