diff --git a/src/main/java/org/mutabilitydetector/checkers/AccessModifierQuery.java b/src/main/java/org/mutabilitydetector/checkers/AccessModifierQuery.java index 111001b5..b92fed00 100644 --- a/src/main/java/org/mutabilitydetector/checkers/AccessModifierQuery.java +++ b/src/main/java/org/mutabilitydetector/checkers/AccessModifierQuery.java @@ -22,6 +22,7 @@ import static org.objectweb.asm.Opcodes.ACC_INTERFACE; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; /** * Used to check for the existence of an access flag used in ASM visitors. @@ -77,6 +78,15 @@ public boolean isFinal() { public boolean isNotFinal() { return !is(ACC_FINAL); } + + public boolean isSynthetic() { + return !is(ACC_SYNTHETIC); + } + + public boolean isNotSynthetic() { + return !is(ACC_SYNTHETIC); + } + public boolean isAbstract() { return is(ACC_ABSTRACT); diff --git a/src/main/java/org/mutabilitydetector/checkers/CanSubclassChecker.java b/src/main/java/org/mutabilitydetector/checkers/CanSubclassChecker.java index 947cd5e1..c8baaf2e 100644 --- a/src/main/java/org/mutabilitydetector/checkers/CanSubclassChecker.java +++ b/src/main/java/org/mutabilitydetector/checkers/CanSubclassChecker.java @@ -39,7 +39,7 @@ public void visit(int version, int access, String name, String signature, String @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if (MethodIs.aConstructor(name) && method(access).isNotPrivate()) { + if (MethodIs.aConstructor(name) && (method(access).isNotPrivate()) && method(access).isNotSynthetic()) { hasOnlyPrivateConstructors = false; } return super.visitMethod(access, name, desc, signature, exceptions); diff --git a/src/test/benchmarks/org/mutabilitydetector/benchmarks/ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern.java b/src/test/benchmarks/org/mutabilitydetector/benchmarks/ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern.java new file mode 100644 index 00000000..c7447feb --- /dev/null +++ b/src/test/benchmarks/org/mutabilitydetector/benchmarks/ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern.java @@ -0,0 +1,5 @@ +package org.mutabilitydetector.benchmarks; + +public class ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern { + +} diff --git a/src/test/java/org/mutabilitydetector/benchmarks/CanSubclassCheckerTest.java b/src/test/java/org/mutabilitydetector/benchmarks/CanSubclassCheckerTest.java index 988c0193..bd99158c 100644 --- a/src/test/java/org/mutabilitydetector/benchmarks/CanSubclassCheckerTest.java +++ b/src/test/java/org/mutabilitydetector/benchmarks/CanSubclassCheckerTest.java @@ -42,6 +42,7 @@ import org.mutabilitydetector.benchmarks.types.EnumType; import org.mutabilitydetector.checkers.CanSubclassChecker; import org.mutabilitydetector.locations.ClassLocation; +import org.mutabilitydetector.unittesting.MutabilityAssert; @RunWith(Theories.class) public class CanSubclassCheckerTest { @@ -59,6 +60,7 @@ public void createChecker() { AnalysisResultTheory.of(HasFinalFieldsAndADefaultConstructor.class, NOT_IMMUTABLE), AnalysisResultTheory.of(IsFinalAndHasOnlyPrivateConstructors.class, IMMUTABLE), AnalysisResultTheory.of(ImmutableByHavingOnlyPrivateConstructors.class, IMMUTABLE), + AnalysisResultTheory.of(ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern.class, IMMUTABLE), }; @Theory @@ -90,4 +92,8 @@ public void hasCodeLocationWithCorrectTypeName() throws Exception { assertThat(location.typeName(), is(MutableByNotBeingFinalClass.class.getName())); } + @Test + public void privateConstructorsUsingBuilderPatternAreImmutable() throws Exception { + MutabilityAssert.assertImmutable(ImmutableByHavingOnlyAPrivateConstructorUsingTheBuilderPattern.class); + } }