JDK astubx generator files an inner class of a generic class under the outer class
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 62/100
Direzione di ricerca
Start in jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java, in getAstubxData, where the class key is derived from clazz.type() cut at the first <. Run the two reproducing tests innerClassOfGenericClass and innerClassTypeParamBound in AstubxTest.java to see the missing Outer.Inner record and the wrong Outer2 bound. Done when both tests pass, Outer.Inner gets its own record, and Outer2 no longer receives the inner class's bound.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
The JDK astubx generator files what it records for an inner (non-static) class of a generic class under the outer class's name. The inner class's method models are lost when the outer class declares the same method, and a nullable type-parameter bound of the inner class becomes a bound of the outer class.
I added these two cases to jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java on master (a03b7bb90) and ran ./gradlew :jdk-annotations:astubx-generator:test --tests 'com.uber.nullaway.jdkannotations.AstubxTest':
@Test
public void innerClassOfGenericClass() {
compilationHelper
.addSourceLines(
"Outer.java",
"""
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class Outer<T> {
public @Nullable String get(@Nullable Object key) { return null; }
public class Inner {
public @Nullable String get(Object key) { return null; }
}
public static class Nested {
public @Nullable String get(Object key) { return null; }
}
}
""")
.doTest();
ImmutableMap<String, MethodAnnotationsRecord> expectedMethodRecords =
ImmutableMap.of(
"Outer:java.lang.String get(java.lang.Object)",
MethodAnnotationsRecord.create(
ImmutableSet.of("Nullable"),
ImmutableSet.of(),
ImmutableMap.of(0, ImmutableSet.of("Nullable")),
ImmutableSetMultimap.of()),
"Outer.Inner:java.lang.String get(java.lang.Object)",
MethodAnnotationsRecord.create(
ImmutableSet.of("Nullable"),
ImmutableSet.of(),
ImmutableMap.of(),
ImmutableSetMultimap.of()),
"Outer.Nested:java.lang.String get(java.lang.Object)",
MethodAnnotationsRecord.create(
ImmutableSet.of("Nullable"),
ImmutableSet.of(),
ImmutableMap.of(),
ImmutableSetMultimap.of()));
runTest(expectedMethodRecords, ImmutableMap.of(), ImmutableSet.of("Outer"));
}
@Test
public void innerClassTypeParamBound() {
compilationHelper
.addSourceLines(
"Outer2.java",
"""
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class Outer2<T> {
public class Inner<U extends @Nullable Object> {}
}
""")
.doTest();
runTest(
ImmutableMap.of(),
ImmutableMap.of("Outer2.Inner", ImmutableSet.of(0)),
ImmutableSet.of("Outer2"));
}
Both fail. The record for Outer.Inner is missing, while the static Outer.Nested gets its own record:
AstubxTest > innerClassOfGenericClass FAILED
java.lang.AssertionError:
Expected: <{Outer:java.lang.String get(java.lang.Object)=MethodAnnotationsRecord[methodAnnotations=[Nullable], typeParamNullableUpperbounds=[], argumentAnnotations={0=[Nullable]}, nestedAnnotationInfo={}], Outer.Inner:java.lang.String get(java.lang.Object)=MethodAnnotationsRecord[methodAnnotations=[Nullable], typeParamNullableUpperbounds=[], argumentAnnotations={}, nestedAnnotationInfo={}], Outer.Nested:java.lang.String get(java.lang.Object)=MethodAnnotationsRecord[methodAnnotations=[Nullable], typeParamNullableUpperbounds=[], argumentAnnotations={}, nestedAnnotationInfo={}]}>
but: was <{Outer:java.lang.String get(java.lang.Object)=MethodAnnotationsRecord[methodAnnotations=[Nullable], typeParamNullableUpperbounds=[], argumentAnnotations={0=[Nullable]}, nestedAnnotationInfo={}], Outer.Nested:java.lang.String get(java.lang.Object)=MethodAnnotationsRecord[methodAnnotations=[Nullable], typeParamNullableUpperbounds=[], argumentAnnotations={}, nestedAnnotationInfo={}]}>
Outer2 gets a nullable bound on its type parameter, which only Inner declares:
AstubxTest > innerClassTypeParamBound FAILED
java.lang.AssertionError:
Expected: <{Outer2.Inner=[0]}>
but: was <{Outer2=[0]}>
The committed nullaway/src/main/resources/jspecify-jdk.astubx has such entries. HashMap, LinkedHashMap, and TreeMap declare no boolean contains(Object) or boolean remove(Object), but their inner key-set, entry-set, and values classes do, such as HashMap.KeySet, LinkedHashMap.LinkedEntrySet, and TreeMap.Values:
strings nullaway/src/main/resources/jspecify-jdk.astubx | grep -E '(HashMap|TreeMap):boolean (contains|remove)\(java\.lang\.Object\)'
4java.util.HashMap:boolean contains(java.lang.Object)
2java.util.HashMap:boolean remove(java.lang.Object)
:java.util.LinkedHashMap:boolean contains(java.lang.Object)
8java.util.LinkedHashMap:boolean remove(java.lang.Object)
4java.util.TreeMap:boolean contains(java.lang.Object)
2java.util.TreeMap:boolean remove(java.lang.Object)
These classes are package-private, so I have not found a JDK member whose NullAway diagnostics change because of it. A public generic outer class in the current file does not get a wrong bound this way either.
Likely cause (from reading the code, not confirmed by a fix): AstubxGenerator.getAstubxData derives the class key from clazz.type() cut at the first <. For an inner class of a generic class that string is Outer<T>.Inner, so the key becomes Outer. A static nested class prints as Outer.Nested, which has no < before the nested name. Method records and bounds are then stored with put, so the record written last for a key wins.
Assisted-by: Claude Code (claude-opus-5-5)
- Lingua principale
- Java
- Stelle
- 4.1k
- Fork
- 381
- Merge medio
- 1g 2h
- PR unite (30g)
- 106
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di uber/NullAway
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
uber/NullAway#2145 · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 85/100
uber/NullAway#2058 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
uber/NullAway#1899 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 15/100
uber/NullAway#2160 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 50/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di uber/NullAway
Issue simili
-
Cleanup: restore the interrupt flag in AiFailureAnalyzerForse già presa @Rishankgupta08 l’ha presa oggi. Apertagood first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
seleniumboot/selenium-boot#258 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
dense-analysis/ale#5181 ·
-
bug subsystem:agent-harness triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
ShaftHQ/SHAFT_ENGINE#8043 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
johanhaleby/occurrent#1505 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno