[Git][java-team/checker-framework-java][main] 2 commits: * Cherry-pick upstream patches to resolve Java 25 ftbfs (LP: #2142432): -...

Olek Wojnar (@olek) gitlab at salsa.debian.org
Mon Mar 23 03:21:41 GMT 2026



Olek Wojnar pushed to branch main at Debian Java Maintainers / checker-framework-java


Commits:
854f3e23 by Vladimir Petko at 2026-02-25T22:30:47+13:00
* Cherry-pick upstream patches to resolve Java 25 ftbfs (LP: #2142432): - d/p/improve-attribute-equals.patch - d/p/java25-compat.patch - d/p/prevent-duplicate-annotations-in-class-files.patch

- - - - -
e30039b8 by Vladimir Petko at 2026-02-25T22:32:25+13:00
changelog

- - - - -


5 changed files:

- debian/changelog
- + debian/patches/improve-attribute-equals.patch
- + debian/patches/java25-compat.patch
- + debian/patches/prevent-duplicate-annotations-in-class-files.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+checker-framework-java (3.2.0+ds-4) UNRELEASED; urgency=medium
+
+  * Cherry-pick upstream patches to resolve Java 25 ftbfs
+    (LP: #2142432) (Closes: #1128165):
+    - d/p/improve-attribute-equals.patch
+    - d/p/java25-compat.patch
+    - d/p/prevent-duplicate-annotations-in-class-files.patch
+
+ -- Vladimir Petko <vladimir.petko at canonical.com>  Wed, 25 Feb 2026 22:31:02 +1300
+
 checker-framework-java (3.2.0+ds-3) unstable; urgency=medium
 
   * Team upload


=====================================
debian/patches/improve-attribute-equals.patch
=====================================
@@ -0,0 +1,206 @@
+From 66ba31a59cd369db0bd70fc000948281ab0fb334 Mon Sep 17 00:00:00 2001
+From: Michael Ernst <mernst at cs.washington.edu>
+Date: Wed, 16 Dec 2020 09:28:15 -0800
+Subject: [PATCH] Improve attributeEquals (#4026)
+Origin: upstream, https://github.com/typetools/checker-framework/commit/66ba31a59cd369db0bd70fc000948281ab0fb334
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128165
+Bug-Ubuntu: https://bugs.launchpad.net/debian/+source/checker-framework-java/+bug/2142432
+
+---
+ .../javacutil/TypeAnnotationUtils.java        | 96 +++++++++++++++----
+ 1 file changed, 78 insertions(+), 18 deletions(-)
+
+diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
+index f497c6580..62b0895cc 100644
+--- a/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
++++ b/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
+@@ -1,7 +1,6 @@
+ package org.checkerframework.javacutil;
+ 
+ import com.sun.tools.javac.code.Attribute;
+-import com.sun.tools.javac.code.Attribute.Constant;
+ import com.sun.tools.javac.code.Attribute.TypeCompound;
+ import com.sun.tools.javac.code.Symbol;
+ import com.sun.tools.javac.code.Symbol.MethodSymbol;
+@@ -12,7 +11,6 @@ import com.sun.tools.javac.util.Context;
+ import com.sun.tools.javac.util.List;
+ import com.sun.tools.javac.util.Name;
+ import com.sun.tools.javac.util.Pair;
+-import java.nio.ByteBuffer;
+ import java.util.Arrays;
+ import java.util.Iterator;
+ import java.util.Map;
+@@ -24,10 +22,12 @@ import javax.lang.model.element.ElementKind;
+ import javax.lang.model.element.ExecutableElement;
+ import javax.lang.model.element.VariableElement;
+ import javax.lang.model.type.ArrayType;
++import javax.lang.model.type.DeclaredType;
+ import javax.lang.model.type.TypeKind;
+ import javax.lang.model.type.TypeMirror;
+ import javax.lang.model.util.Elements;
+ import javax.lang.model.util.Types;
++import org.checkerframework.checker.nullness.qual.NonNull;
+ 
+ /**
+  * A collection of helper methods related to type annotation handling.
+@@ -46,12 +46,13 @@ public class TypeAnnotationUtils {
+      *
+      * @param list the input list of TypeCompounds
+      * @param tc the TypeCompound to find
++     * @param types type utilities
+      * @return true, iff a TypeCompound equal to tc is contained in list
+      */
+     public static boolean isTypeCompoundContained(
+             List<TypeCompound> list, TypeCompound tc, Types types) {
+         for (Attribute.TypeCompound rawat : list) {
+-            if (typeCompoundEquals(rawat, tc)) {
++            if (typeCompoundEquals(rawat, tc, types)) {
+                 return true;
+             }
+         }
+@@ -63,14 +64,15 @@ public class TypeAnnotationUtils {
+      *
+      * @param tc1 the first TypeCompound to compare
+      * @param tc2 the second TypeCompound to compare
++     * @param types type utilities
+      * @return true if the TypeCompounds represent the same compound element value
+      */
+-    private static boolean typeCompoundEquals(TypeCompound tc1, TypeCompound tc2) {
++    private static boolean typeCompoundEquals(TypeCompound tc1, TypeCompound tc2, Types types) {
+         // For the first conjunct, both of these forms fail in some cases:
+         //   tc1.type == tc2.type
+         //   types.isSameType(tc1.type, tc2.type)
+         return contentEquals(tc1.type.tsym.name, tc2.type.tsym.name)
+-                && typeCompoundValuesEquals(tc1.values, tc2.values)
++                && typeCompoundValuesEquals(tc1.values, tc2.values, types)
+                 && isSameTAPositionExceptTreePos(tc1.position, tc2.position);
+     }
+ 
+@@ -81,12 +83,17 @@ public class TypeAnnotationUtils {
+      * @param n2 the second Name to compare
+      * @return true if the two names represent the same string
+      */
++    @SuppressWarnings(
++            "interning:unnecessary.equals" // Name is interned within a single instance of javac,
++    // but call equals anyway out of paranoia.
++    )
+     private static boolean contentEquals(Name n1, Name n2) {
+-        // Views of underlying bytes, not copies as with Name#contentEquals
+-        ByteBuffer b1 = ByteBuffer.wrap(n1.getByteArray(), n1.getByteOffset(), n1.getByteLength());
+-        ByteBuffer b2 = ByteBuffer.wrap(n2.getByteArray(), n2.getByteOffset(), n2.getByteLength());
+-
+-        return b1.equals(b2);
++        if (n1.getClass() == n2.getClass()) {
++            return n1.equals(n2);
++        } else {
++            // Slightly less efficient because it makes a copy.
++            return n1.contentEquals(n2);
++        }
+     }
+ 
+     /**
+@@ -95,13 +102,15 @@ public class TypeAnnotationUtils {
+      *
+      * @param values1 the first {@code values} field
+      * @param values2 the second {@code values} field
++     * @param types type utilities
+      * @return true if the two {@code values} fields represent the same name-to-value mapping, in
+      *     the same order
+      */
+     @SuppressWarnings("InvalidParam") // Error Prone tries to be clever, but it is not
+     private static boolean typeCompoundValuesEquals(
+             List<Pair<MethodSymbol, Attribute>> values1,
+-            List<Pair<MethodSymbol, Attribute>> values2) {
++            List<Pair<MethodSymbol, Attribute>> values2,
++            Types types) {
+         if (values1.size() != values2.size()) {
+             return false;
+         }
+@@ -111,7 +120,7 @@ public class TypeAnnotationUtils {
+                 iter1.hasNext(); ) {
+             Pair<MethodSymbol, Attribute> pair1 = iter1.next();
+             Pair<MethodSymbol, Attribute> pair2 = iter2.next();
+-            if (!(pair1.fst.equals(pair2.fst) && attributeEquals(pair1.snd, pair2.snd))) {
++            if (!(pair1.fst.equals(pair2.fst) && attributeEquals(pair1.snd, pair2.snd, types))) {
+                 return false;
+             }
+         }
+@@ -119,18 +128,69 @@ public class TypeAnnotationUtils {
+     }
+ 
+     /**
+-     * Compares two attributes. Is more lenient for constants than {@code equals}, which is
+-     * reference equality.
++     * Compares two attributes. Is more lenient for constants than {@code Attribute.equals}, which
++     * is reference equality.
+      *
+      * @param a1 the first attribute to compare
+      * @param a2 the second attribute to compare
++     * @param types type utilities
+      * @return true if the two attributes are the same
+      */
+-    private static boolean attributeEquals(Attribute a1, Attribute a2) {
+-        if (a1 instanceof Constant && a2 instanceof Constant) {
+-            Object v1 = ((Constant) a1).getValue();
+-            Object v2 = ((Constant) a1).getValue();
++    private static boolean attributeEquals(Attribute a1, Attribute a2, Types types) {
++        if (a1 instanceof Attribute.Array && a2 instanceof Attribute.Array) {
++            List<Attribute> list1 = ((Attribute.Array) a1).getValue();
++            List<Attribute> list2 = ((Attribute.Array) a2).getValue();
++            if (list1.size() != list2.size()) {
++                return false;
++            }
++            // This requires the array elements to be in the same order.  Is that the right thing?
++            for (int i = 0; i < list1.size(); i++) {
++                if (!attributeEquals(list1.get(i), list2.get(i), types)) {
++                    return false;
++                }
++            }
++            return true;
++        } else if (a1 instanceof Attribute.Class && a2 instanceof Attribute.Class) {
++            Type t1 = ((Attribute.Class) a1).getValue();
++            Type t2 = ((Attribute.Class) a2).getValue();
++            return types.isSameType(t1, t2);
++        } else if (a1 instanceof Attribute.Constant && a2 instanceof Attribute.Constant) {
++            Object v1 = ((Attribute.Constant) a1).getValue();
++            Object v2 = ((Attribute.Constant) a2).getValue();
+             return v1.equals(v2);
++        } else if (a1 instanceof Attribute.Compound && a2 instanceof Attribute.Compound) {
++            // The annotation value is another annotation.  `a1` and `a2` implement
++            // AnnotationMirror.
++            DeclaredType t1 = ((Attribute.Compound) a1).getAnnotationType();
++            DeclaredType t2 = ((Attribute.Compound) a2).getAnnotationType();
++            if (!types.isSameType(t1, t2)) {
++                return false;
++            }
++            Map<Symbol.MethodSymbol, Attribute> map1 = ((Attribute.Compound) a1).getElementValues();
++            Map<Symbol.MethodSymbol, Attribute> map2 = ((Attribute.Compound) a2).getElementValues();
++            // Is this test, which uses equals() for the keys, too strict?
++            if (!map1.keySet().equals(map2.keySet())) {
++                return false;
++            }
++            for (Symbol.MethodSymbol key : map1.keySet()) {
++                Attribute attr1 = map1.get(key);
++                @SuppressWarnings(
++                        "nullness:assignment.type.incompatible") // same keys in map1 & map2
++                @NonNull Attribute attr2 = map2.get(key);
++                if (!attributeEquals(attr1, attr2, types)) {
++                    return false;
++                }
++            }
++            return true;
++        } else if (a1 instanceof Attribute.Enum && a2 instanceof Attribute.Enum) {
++            Symbol.VarSymbol s1 = ((Attribute.Enum) a1).getValue();
++            Symbol.VarSymbol s2 = ((Attribute.Enum) a2).getValue();
++            // VarSymbol.equals() is reference equality.
++            return s1.equals(s2) || s1.toString().equals(s2.toString());
++        } else if (a1 instanceof Attribute.Error && a2 instanceof Attribute.Error) {
++            String s1 = ((Attribute.Error) a1).getValue();
++            String s2 = ((Attribute.Error) a2).getValue();
++            return s1.equals(s2);
+         } else {
+             return a1.equals(a2);
+         }
+-- 
+2.51.0
+


=====================================
debian/patches/java25-compat.patch
=====================================
@@ -0,0 +1,120 @@
+From 9bf0c63c47e7bfcfa80eec5feea1f3b6556df0aa Mon Sep 17 00:00:00 2001
+From: Suzanne Millstein <smillst at cs.washington.edu>
+Date: Wed, 25 Jun 2025 17:29:28 -0700
+Subject: [PATCH] Make the Checker Framework compile with/run on JDK 25
+Origin: backport, https://github.com/typetools/checker-framework/commit/9bf0c63c47e7bfcfa80eec5feea1f3b6556df0aa
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128165
+Bug-Ubuntu: https://bugs.launchpad.net/debian/+source/checker-framework-java/+bug/2142432
+
+--- a/javacutil/src/main/java/org/checkerframework/javacutil/Resolver.java
++++ b/javacutil/src/main/java/org/checkerframework/javacutil/Resolver.java
+@@ -18,6 +18,7 @@
+ import com.sun.tools.javac.util.Context;
+ import com.sun.tools.javac.util.List;
+ import com.sun.tools.javac.util.Log;
++import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
+ import com.sun.tools.javac.util.Name;
+ import com.sun.tools.javac.util.Names;
+ import java.lang.reflect.Constructor;
+@@ -55,6 +56,13 @@
+     // Note that currently access(...) is defined in InvalidSymbolError, a superclass of AccessError
+     private static final Method ACCESSERROR_ACCESS;
+ 
++    /**
++    * Method for new Log.DiscardDiagnosticHandler. Before JDK 25, DiscardDiagnosticHandler was a
++    * static inner class of Log and an instance of log was passed as the first argument. Starting
++    * with JDK 25, DiscardDiagnosticHandler is an inner class of log.
++    */
++    private static final Constructor<DiscardDiagnosticHandler> NEW_DIAGNOSTIC_HANDLER;
++
+     static {
+         try {
+             FIND_METHOD =
+@@ -97,6 +105,14 @@
+ 
+             FIND_TYPE = Resolve.class.getDeclaredMethod("findType", Env.class, Name.class);
+             FIND_TYPE.setAccessible(true);
++
++            // Pre JDK 25:
++            //   new Log.DiscardDiagnosticHandler(log)
++            // JDK 25:
++            //   log.new DiscardDiagnosticHandler()
++            // But both of those are reflectively accessed the same way.
++            NEW_DIAGNOSTIC_HANDLER = Log.DiscardDiagnosticHandler.class.getConstructor(Log.class);
++            NEW_DIAGNOSTIC_HANDLER.setAccessible(true);
+         } catch (Exception e) {
+             Error err =
+                     new AssertionError(
+@@ -160,7 +176,7 @@
+      * @return the {@code PackageSymbol} for the package if it is found, {@code null} otherwise
+      */
+     public @Nullable PackageSymbol findPackage(String name, TreePath path) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+             Element res =
+@@ -192,7 +208,7 @@
+      * @return the element for the field, {@code null} otherwise
+      */
+     public @Nullable VariableElement findField(String name, TypeMirror type, TreePath path) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+             Element res =
+@@ -226,7 +242,7 @@
+      */
+     public @Nullable VariableElement findLocalVariableOrParameterOrField(
+             String name, TreePath path) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+             Element res = wrapInvocationOnResolveInstance(FIND_VAR, env, names.fromString(name));
+@@ -254,7 +270,7 @@
+      * @return the element for the class
+      */
+     public Element findClass(String name, TreePath path) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+             return wrapInvocationOnResolveInstance(FIND_TYPE, env, names.fromString(name));
+@@ -272,7 +288,7 @@
+      * @return the {@code ClassSymbol} for the class if it is found, {@code null} otherwise
+      */
+     public @Nullable ClassSymbol findClassInPackage(String name, PackageSymbol pck, TreePath path) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+             Element res =
+@@ -308,7 +324,7 @@
+             TypeMirror receiverType,
+             TreePath path,
+             java.util.List<TypeMirror> argumentTypes) {
+-        Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
++        Log.DiagnosticHandler discardDiagnosticHandler = newDiagnosticHandler();
+         try {
+             Env<AttrContext> env = getEnvForPath(path);
+ 
+@@ -411,4 +427,18 @@
+                     e);
+         }
+     }
++
++
++  /**
++   * Creates a new {@code DiscardDiagnosticHandler} with the current {@code log}.
++   *
++   * @return a new {@code DiscardDiagnosticHandler} with the current {@code log}
++   */
++    private Log.DiscardDiagnosticHandler newDiagnosticHandler() {
++        try {
++            return NEW_DIAGNOSTIC_HANDLER.newInstance(log);
++        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
++            throw new BugInCF(e);
++        }
++    }
+ }


=====================================
debian/patches/prevent-duplicate-annotations-in-class-files.patch
=====================================
@@ -0,0 +1,132 @@
+From 971907223b3a8f51eac519f57f34183e4495e621 Mon Sep 17 00:00:00 2001
+From: Michael Ernst <mernst at cs.washington.edu>
+Date: Wed, 9 Dec 2020 12:55:39 -0800
+Subject: [PATCH] Prevent duplicate annotations in .class files; fixes #3956
+Origin: backport, https://github.com/typetools/checker-framework/commit/971907223b3a8f51eac519f57f34183e4495e621
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128165
+Bug-Ubuntu: https://bugs.launchpad.net/debian/+source/checker-framework-java/+bug/2142432
+
+---
+ .../javacutil/TypeAnnotationUtils.java        | 82 +++++++++++++++++--
+ 1 file changed, 74 insertions(+), 8 deletions(-)
+
+--- a/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
++++ b/javacutil/src/main/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
+@@ -1,8 +1,10 @@
+ package org.checkerframework.javacutil;
+ 
+ import com.sun.tools.javac.code.Attribute;
++import com.sun.tools.javac.code.Attribute.Constant;
+ import com.sun.tools.javac.code.Attribute.TypeCompound;
+ import com.sun.tools.javac.code.Symbol;
++import com.sun.tools.javac.code.Symbol.MethodSymbol;
+ import com.sun.tools.javac.code.Type;
+ import com.sun.tools.javac.code.TypeAnnotationPosition;
+ import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+@@ -12,6 +14,7 @@
+ import com.sun.tools.javac.util.Pair;
+ import java.nio.ByteBuffer;
+ import java.util.Arrays;
++import java.util.Iterator;
+ import java.util.Map;
+ import javax.annotation.processing.ProcessingEnvironment;
+ import javax.lang.model.element.AnnotationMirror;
+@@ -48,20 +51,36 @@
+     public static boolean isTypeCompoundContained(
+             List<TypeCompound> list, TypeCompound tc, Types types) {
+         for (Attribute.TypeCompound rawat : list) {
+-            if (contentEquals(rawat.type.tsym.name, tc.type.tsym.name)
+-                    // TODO: in previous line, it would be nicer to use reference equality:
+-                    //   rawat.type == tc.type &&
+-                    // or at least "isSameType":
+-                    //   types.isSameType(rawat.type, tc.type) &&
+-                    // but each fails in some cases.
+-                    && rawat.values.equals(tc.values)
+-                    && isSameTAPositionExceptTreePos(rawat.position, tc.position)) {
++            if (typeCompoundEquals(rawat, tc)) {
+                 return true;
+             }
+         }
+         return false;
+     }
+ 
++    /**
++     * Compares two TypeCompound objects (e.g., annotations).
++     *
++     * @param tc1 the first TypeCompound to compare
++     * @param tc2 the second TypeCompound to compare
++     * @return true if the TypeCompounds represent the same compound element value
++     */
++    private static boolean typeCompoundEquals(TypeCompound tc1, TypeCompound tc2) {
++        // For the first conjunct, both of these forms fail in some cases:
++        //   tc1.type == tc2.type
++        //   types.isSameType(tc1.type, tc2.type)
++        return contentEquals(tc1.type.tsym.name, tc2.type.tsym.name)
++                && typeCompoundValuesEquals(tc1.values, tc2.values)
++                && isSameTAPositionExceptTreePos(tc1.position, tc2.position);
++    }
++
++    /**
++     * Returns true if the two names represent the same string.
++     *
++     * @param n1 the first Name to compare
++     * @param n2 the second Name to compare
++     * @return true if the two names represent the same string
++     */
+     private static boolean contentEquals(Name n1, Name n2) {
+         // Views of underlying bytes, not copies as with Name#contentEquals
+         ByteBuffer b1 = ByteBuffer.wrap(n1.getByteArray(), n1.getByteOffset(), n1.getByteLength());
+@@ -71,6 +90,53 @@
+     }
+ 
+     /**
++     * Compares the {@code values} fields of two TypeCompound objects (e.g., annotations). Is more
++     * lenient than {@code List.equals}, which uses {@code Object.equals} on list elements.
++     *
++     * @param values1 the first {@code values} field
++     * @param values2 the second {@code values} field
++     * @return true if the two {@code values} fields represent the same name-to-value mapping, in
++     *     the same order
++     */
++    @SuppressWarnings("InvalidParam") // Error Prone tries to be clever, but it is not
++    private static boolean typeCompoundValuesEquals(
++            List<Pair<MethodSymbol, Attribute>> values1,
++            List<Pair<MethodSymbol, Attribute>> values2) {
++        if (values1.size() != values2.size()) {
++            return false;
++        }
++
++        for (Iterator<Pair<MethodSymbol, Attribute>> iter1 = values1.iterator(),
++                        iter2 = values2.iterator();
++                iter1.hasNext(); ) {
++            Pair<MethodSymbol, Attribute> pair1 = iter1.next();
++            Pair<MethodSymbol, Attribute> pair2 = iter2.next();
++            if (!(pair1.fst.equals(pair2.fst) && attributeEquals(pair1.snd, pair2.snd))) {
++                return false;
++            }
++        }
++        return true;
++    }
++
++    /**
++     * Compares two attributes. Is more lenient for constants than {@code equals}, which is
++     * reference equality.
++     *
++     * @param a1 the first attribute to compare
++     * @param a2 the second attribute to compare
++     * @return true if the two attributes are the same
++     */
++    private static boolean attributeEquals(Attribute a1, Attribute a2) {
++        if (a1 instanceof Constant && a2 instanceof Constant) {
++            Object v1 = ((Constant) a1).getValue();
++            Object v2 = ((Constant) a1).getValue();
++            return v1.equals(v2);
++        } else {
++            return a1.equals(a2);
++        }
++    }
++
++    /**
+      * Compare two TypeAnnotationPositions for equality.
+      *
+      * @param p1 the first position


=====================================
debian/patches/series
=====================================
@@ -2,3 +2,6 @@ fix-gradle-build.patch
 remove-doc-image-links.patch
 deprecated-getexpression.patch
 fix-javac-21-warnings.patch
+prevent-duplicate-annotations-in-class-files.patch
+java25-compat.patch
+improve-attribute-equals.patch



View it on GitLab: https://salsa.debian.org/java-team/checker-framework-java/-/compare/0543642d68658a9c1c6f10191bb5f08d75044b0b...e30039b8b55bc800494f97deb2326fbe16594fbc

-- 
View it on GitLab: https://salsa.debian.org/java-team/checker-framework-java/-/compare/0543642d68658a9c1c6f10191bb5f08d75044b0b...e30039b8b55bc800494f97deb2326fbe16594fbc
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20260323/faa8e383/attachment.htm>


More information about the pkg-java-commits mailing list