diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaModifier.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaModifier.java index b5b4755656..6692f1d7a4 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaModifier.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaModifier.java @@ -46,7 +46,11 @@ public enum JavaModifier { @PublicAPI(usage = ACCESS) SYNCHRONIZED(EnumSet.of(ApplicableType.METHOD), Opcodes.ACC_SYNCHRONIZED), @PublicAPI(usage = ACCESS) - NATIVE(EnumSet.of(ApplicableType.METHOD), Opcodes.ACC_NATIVE); + NATIVE(EnumSet.of(ApplicableType.METHOD), Opcodes.ACC_NATIVE), + @PublicAPI(usage = ACCESS) + BRIDGE(EnumSet.of(ApplicableType.METHOD), Opcodes.ACC_BRIDGE), + @PublicAPI(usage = ACCESS) + SYNTHETIC(EnumSet.allOf(ApplicableType.class), Opcodes.ACC_SYNTHETIC); private final Set applicableTo; private final int asmAccessFlag; diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java index 30278420f7..3b3bb4a1ba 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java @@ -170,6 +170,7 @@ import com.tngtech.archunit.core.importer.testexamples.simpleimport.InterfaceToImport; import com.tngtech.archunit.core.importer.testexamples.simplenames.SimpleNameExamples; import com.tngtech.archunit.core.importer.testexamples.specialtargets.ClassCallingSpecialTarget; +import com.tngtech.archunit.core.importer.testexamples.syntheticimport.ClassWithSynthetics; import com.tngtech.archunit.testutil.LogTestRule; import com.tngtech.archunit.testutil.OutsideOfClassPathRule; import com.tngtech.java.junit.dataprovider.DataProvider; @@ -194,11 +195,13 @@ import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME; import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.GET; import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.SET; +import static com.tngtech.archunit.core.domain.JavaModifier.BRIDGE; import static com.tngtech.archunit.core.domain.JavaModifier.FINAL; import static com.tngtech.archunit.core.domain.JavaModifier.PRIVATE; import static com.tngtech.archunit.core.domain.JavaModifier.PROTECTED; import static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC; import static com.tngtech.archunit.core.domain.JavaModifier.STATIC; +import static com.tngtech.archunit.core.domain.JavaModifier.SYNTHETIC; import static com.tngtech.archunit.core.domain.JavaModifier.TRANSIENT; import static com.tngtech.archunit.core.domain.JavaModifier.VOLATILE; import static com.tngtech.archunit.core.domain.JavaStaticInitializer.STATIC_INITIALIZER_NAME; @@ -384,6 +387,20 @@ public void handles_static_modifier_of_nested_classes() throws Exception { assertThat(classes.get(ClassWithNestedClass.StaticNestedInterface.class).getModifiers()).as("modifiers of ClassWithNestedClass.StaticNestedInterface").contains(STATIC); } + @Test + public void handles_synthetic_modifiers() throws Exception { + JavaClasses classes = classesIn("testexamples/syntheticimport").classes; + + JavaField syntheticField = getOnlyElement(classes.get(ClassWithSynthetics.ClassWithSyntheticField.class).getFields()); + assertThat(syntheticField.getModifiers()).as("modifiers of field in ClassWithSynthetics.ClassWithSyntheticField").contains(SYNTHETIC); + + JavaMethod syntheticMethod = getOnlyElement(classes.get(ClassWithSynthetics.ClassWithSyntheticMethod.class).getMethods()); + assertThat(syntheticMethod.getModifiers()).as("modifiers of method in ClassWithSynthetics.ClassWithSyntheticMethod").contains(SYNTHETIC); + + JavaMethod compareMethod = classes.get(ClassWithSynthetics.class).getMethod("compare", Object.class, Object.class); + assertThat(compareMethod.getModifiers()).as("modifiers of bridge method in ClassWithSynthetics").contains(BRIDGE, SYNTHETIC); + } + @Test public void imports_jdk_classes() { JavaClasses classes = new ClassFileImporter().importClasses(File.class); diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/syntheticimport/ClassWithSynthetics.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/syntheticimport/ClassWithSynthetics.java new file mode 100644 index 0000000000..b5037de217 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/syntheticimport/ClassWithSynthetics.java @@ -0,0 +1,26 @@ +package com.tngtech.archunit.core.importer.testexamples.syntheticimport; + +import java.util.Comparator; + +@SuppressWarnings({"unused", "InnerClassMayBeStatic"}) +public class ClassWithSynthetics implements Comparator { + // for (non-static) inner classes the compiler must create a synthetic field, holding a reference to the outer class + public class ClassWithSyntheticField { + } + + // for accesses to private fields of inner classes, the compiler must create a synthetic method to allow access to this field + // thus together with the method 'getNestedField', this causes the existence of a synthetic method + public class ClassWithSyntheticMethod { + private String nestedField; + } + + public String getNestedField() { + return new ClassWithSyntheticMethod().nestedField; + } + + // to cover type erasure, the compiler must add a bridge method with signature compare(Object, Object) here + @Override + public int compare(String o1, String o2) { + return 0; + } +}