Skip to content

Commit 9b3a512

Browse files
committed
support type cast detection #710
Detect checkcast asm instructions as TypeCast (similar to InstanceofCheck) Implicit checkcast instructions generated by compiler due to method invocations are ignored Resolves #710 Signed-off-by: Allan Jones <[email protected]>
1 parent e87cd32 commit 9b3a512

34 files changed

+698
-3
lines changed

archunit-example/example-junit4/src/test/resources/frozen/e77ec262-4d5c-4a7b-b41f-362a71e5a1d8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ Method <com.tngtech.archunit.example.layers.persistence.first.dao.jpa.SomeJpa.fi
66
Method <com.tngtech.archunit.example.layers.persistence.second.dao.OtherDao.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherDao.java:0)
77
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (OtherJpa.java:19)
88
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherJpa.java:0)
9-
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
9+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
10+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> casts <com.tngtech.archunit.example.layers.service.ProxiedConnection> in (OtherJpa.java:27)

archunit-example/example-junit5/src/test/resources/frozen/e77ec262-4d5c-4a7b-b41f-362a71e5a1d8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ Method <com.tngtech.archunit.example.layers.persistence.first.dao.jpa.SomeJpa.fi
66
Method <com.tngtech.archunit.example.layers.persistence.second.dao.OtherDao.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherDao.java:0)
77
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (OtherJpa.java:19)
88
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherJpa.java:0)
9-
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
9+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
10+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> casts <com.tngtech.archunit.example.layers.service.ProxiedConnection> in (OtherJpa.java:27)

archunit-example/example-plain/src/test/resources/frozen/e77ec262-4d5c-4a7b-b41f-362a71e5a1d8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ Method <com.tngtech.archunit.example.layers.persistence.first.dao.jpa.SomeJpa.fi
66
Method <com.tngtech.archunit.example.layers.persistence.second.dao.OtherDao.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherDao.java:0)
77
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (OtherJpa.java:19)
88
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherJpa.java:0)
9-
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
9+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
10+
Method <com.tngtech.archunit.example.layers.persistence.second.dao.jpa.OtherJpa.testConnection()> casts <com.tngtech.archunit.example.layers.service.ProxiedConnection> in (OtherJpa.java:27)

archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ Stream<DynamicTest> FrozenRulesTest() {
672672
.by(method(OtherJpa.class, "testConnection")
673673
.checkingInstanceOf(ProxiedConnection.class)
674674
.inLine(26))
675+
.by(method(OtherJpa.class, "testConnection")
676+
.casting(ProxiedConnection.class)
677+
.inLine(27))
675678

676679
.ofRule("no classes should depend on classes that are assignable to javax.persistence.EntityManager")
677680
.by(callFromMethod(ServiceViolatingDaoRules.class, "illegallyUseEntityManager").
@@ -847,6 +850,9 @@ Stream<DynamicTest> LayerDependencyRulesTest() {
847850
.by(method(OtherJpa.class, "testConnection")
848851
.checkingInstanceOf(ProxiedConnection.class)
849852
.inLine(26))
853+
.by(method(OtherJpa.class, "testConnection")
854+
.casting(ProxiedConnection.class)
855+
.inLine(27))
850856

851857
.ofRule("classes that reside in a package '..service..' should " +
852858
"only have dependent classes that reside in any package ['..controller..', '..service..']")
@@ -866,6 +872,9 @@ Stream<DynamicTest> LayerDependencyRulesTest() {
866872
.by(method(OtherJpa.class, "testConnection")
867873
.checkingInstanceOf(ProxiedConnection.class)
868874
.inLine(26))
875+
.by(method(OtherJpa.class, "testConnection")
876+
.casting(ProxiedConnection.class)
877+
.inLine(27))
869878

870879
.ofRule("classes that reside in a package '..service..' should "
871880
+ "only depend on classes that reside in any package ['..service..', '..persistence..', 'java..', 'javax..']")
@@ -972,6 +981,9 @@ Stream<DynamicTest> LayeredArchitectureTest() {
972981
.toMethod(ProxiedConnection.class, "refresh")
973982
.inLine(27)
974983
.asDependency())
984+
.by(method(OtherJpa.class, "testConnection")
985+
.casting(ProxiedConnection.class)
986+
.inLine(27))
975987

976988
.by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class))
977989
.by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class))

archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedDependency.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ public AddsLineNumber checkingInstanceOf(Class<?> target) {
295295
return new AddsLineNumber(owner, getOriginName(), "checks instanceof", target);
296296
}
297297

298+
public AddsLineNumber casting(Class<?> target) {
299+
return new AddsLineNumber(owner, getOriginName(), "casts", target);
300+
}
301+
298302
public AddsLineNumber referencingClassObject(Class<?> target) {
299303
return new AddsLineNumber(owner, getOriginName(), "references class object", target);
300304
}

archunit/src/main/java/com/tngtech/archunit/core/domain/Dependency.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ static Set<Dependency> tryCreateFromInstanceofCheck(InstanceofCheck instanceofCh
128128
instanceofCheck.getRawType(), instanceofCheck.getSourceCodeLocation());
129129
}
130130

131+
static Set<Dependency> tryCreateFromTypeCast(TypeCast typeCast) {
132+
return tryCreateDependency(
133+
typeCast.getOwner(), "casts",
134+
typeCast.getRawType(), typeCast.getSourceCodeLocation());
135+
}
136+
131137
static Set<Dependency> tryCreateFromReferencedClassObject(ReferencedClassObject referencedClassObject) {
132138
return tryCreateDependency(
133139
referencedClassObject.getOwner(), "references class object",

archunit/src/main/java/com/tngtech/archunit/core/domain/DomainObjectCreationContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ public static InstanceofCheck createInstanceofCheck(JavaCodeUnit codeUnit, JavaC
184184
return InstanceofCheck.from(codeUnit, type, lineNumber, declaredInLambda);
185185
}
186186

187+
public static TypeCast createTypeCast(JavaCodeUnit codeUnit, JavaClass type, int lineNumber, boolean declaredInLambda) {
188+
return TypeCast.from(codeUnit, type, lineNumber, declaredInLambda);
189+
}
190+
187191
public static <OWNER extends HasDescription> JavaTypeVariable<OWNER> createTypeVariable(String name, OWNER owner, JavaClass erasure) {
188192
return new JavaTypeVariable<>(name, owner, erasure);
189193
}

archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,7 @@ public interface ImportContext {
6767

6868
Set<InstanceofCheck> createInstanceofChecksFor(JavaCodeUnit codeUnit);
6969

70+
Set<TypeCast> createTypeCastsFor(JavaCodeUnit codeUnit);
71+
7072
JavaClass resolveClass(String fullyQualifiedClassName);
7173
}

archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,11 @@ public Set<InstanceofCheck> getInstanceofChecks() {
645645
return members.getInstanceofChecks();
646646
}
647647

648+
@PublicAPI(usage = ACCESS)
649+
public Set<TypeCast> getTypeCasts() {
650+
return members.getTypeCasts();
651+
}
652+
648653
@PublicAPI(usage = ACCESS)
649654
public Set<ReferencedClassObject> getReferencedClassObjects() {
650655
return members.getReferencedClassObjects();

archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ private Supplier<Set<Dependency>> createDirectDependenciesFromClassSupplier() {
4949
throwsDeclarationDependenciesFromSelf(),
5050
annotationDependenciesFromSelf(),
5151
instanceofCheckDependenciesFromSelf(),
52+
typeCastDependenciesFromSelf(),
5253
referencedClassObjectDependenciesFromSelf(),
5354
typeParameterDependenciesFromSelf()
5455
).collect(toImmutableSet())
@@ -182,6 +183,11 @@ private Stream<Dependency> instanceofCheckDependenciesFromSelf() {
182183
.flatMap(instanceofCheck -> Dependency.tryCreateFromInstanceofCheck(instanceofCheck).stream());
183184
}
184185

186+
private Stream<Dependency> typeCastDependenciesFromSelf() {
187+
return javaClass.getTypeCasts().stream()
188+
.flatMap(typeCast -> Dependency.tryCreateFromTypeCast(typeCast).stream());
189+
}
190+
185191
private Stream<Dependency> referencedClassObjectDependenciesFromSelf() {
186192
return javaClass.getReferencedClassObjects().stream()
187193
.flatMap(referencedClassObject -> Dependency.tryCreateFromReferencedClassObject(referencedClassObject).stream());

0 commit comments

Comments
 (0)