`getAllInvolvedRawTypes()` sometimes returns incorrect type variable bounds
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 42/100
Research direction
The report identifies getAllInvolvedRawTypes() and provides a reproducing JUnit test in TypeVariableTest, including nested classes with shadowed type parameters. Start by locating that method and tracing how type variables in imported supertypes resolve their bounds; use the supplied test scenario to reproduce the issue. Done means the test’s expected bounds are reported for every nested class, with zero assertion failures.
Written by the indexing model from the issue text.
Description
Scenario:
- a generic method or class ("scope" for short) has a type parameter called
Twith type boundsB1, i.e.T extends B1; - this scope has a nested class
Cwith a type parameter also calledT(which shadows theTin the enclosing scope) with type boundsB2; - the type parameter
Tis used in theextendsorimplementesclause of classC, e.g.C<T extends B2> implements List<T>.
Problem:
- when calling
getAllInvolvedRawTypes()on the supertype(s) ofC(in the above example,List<T>), the type parameterTsometimes incorrectly resolves to the shadowed type parameterT(the one in the enclosing scope), and thus its bounds are incorrectly reported asB1instead ofB2.
Full example code:
package typevariabletest;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
class TypeVariableTest {
static class Bound0 {}
static class Bound1 {}
static class Bound2 {}
static class Bound3 {}
static class Bound4 {}
static class Bound5 {}
static class Bound6 {}
static class Bound7 {}
static class Bound8 {}
static class Bound9 {}
static class C0<T extends Bound0> extends ArrayList<T> {
static class C1<T extends Bound1> extends ArrayList<T> {
static class C2<T extends Bound2> extends ArrayList<T> {
static class C3<T extends Bound3> extends ArrayList<T> {
static class C4<T extends Bound4> extends ArrayList<T> {
static class C5<T extends Bound5> extends ArrayList<T> {
static class C6<T extends Bound6> extends ArrayList<T> {
static class C7<T extends Bound7> extends ArrayList<T> {
static class C8<T extends Bound8> extends ArrayList<T> {
static class C9<T extends Bound9> extends ArrayList<T> {
}
}
}
}
}
}
}
}
}
}
@Test
void test() {
final SoftAssertions softly = new SoftAssertions();
final String BASE_CLASS_NAME = TypeVariableTest.class.getName();
final JavaClasses javaClasses = new ClassFileImporter().importPackagesOf(TypeVariableTest.class);
String enclosingClassName = BASE_CLASS_NAME;
for (int i = 0; i <= 9; i++) {
final String className = enclosingClassName + "$C" + i;
final JavaClass javaClass = javaClasses.get(className);
final Set<String> expected = Set.of(
"java.util.ArrayList",
BASE_CLASS_NAME + "$Bound" + i
);
final Set<String> actual = Stream.concat(
javaClass.getInterfaces().stream(),
javaClass.getSuperclass().stream())
.flatMap(supertype -> supertype
.getAllInvolvedRawTypes() // sometimes contains bounds of the *shadowed*
// type parameter, not the actual (shadowing) one,
// e.g. `Bound0` instead of `Bound1`,
// or even `Bound4` instead of `Bound9`
.stream())
.map(JavaClass::getFullName)
.collect(toSet());
softly
.assertThat(actual)
.as(className)
.containsExactlyInAnyOrderElementsOf(expected);
enclosingClassName = className;
}
softly.assertAll();
}
}
The above JUnit test performs 10 soft assertions, of which typically 7 or 8 fail (the expected state is 0 failures).
Tested with ArchUnit 1.4.0.
- Dominant language
- Java
- Stars
- 3.9k
- Forks
- 350
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 22
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TNG/ArchUnit
-
breaking change
Difficulty 5/5 Over a week Newbie friendliness 18/100
TNG/ArchUnit#1756 · 1 comment ·
Maintainers usually reply within 1 day
-
breaking change
Difficulty 4/5 3-5 days Newbie friendliness 30/100
Maintainers usually reply within 1 day
-
breaking change
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Maintainers usually reply within 1 day
-
breaking change
Difficulty 3/5 Half a day Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
breaking change
Difficulty 5/5 Over a week Newbie friendliness 12/100
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
[Bug]: REST API URL starting with "http" but no scheme (e.g. httpbin.org/get) is not given an http:// prefixPossibly taken @ItsDeadlyProgrammer claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
appsmithorg/appsmith#42471 · 1 comment ·
Maintainers usually reply within 2 days
-
code-maintenance
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
bug priority: medium Review effort [1-5]: 3 status: needs-triage type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
carlos-emr/carlos#5258 ·
Maintainers usually reply within 1 day