From dcbece8082487e906d3eae5704060e15c50fc583 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Thu, 11 Apr 2024 21:55:31 +0200 Subject: [PATCH] null analysis should merge null contracts from equivalent super methods fixes #2325 + if method choice would be arbitrary prefer more specific nullness --- .../compiler/ast/NullAnnotationMatching.java | 20 +++++++++++ .../jdt/internal/compiler/lookup/Scope.java | 7 +++- .../regression/NullTypeAnnotationTest.java | 35 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java index bf259468cc6..051cea830b1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java @@ -816,6 +816,26 @@ private static TypeBinding mergeTypeAnnotations(TypeBinding type, TypeBinding ot return mainType; } + /** + * Help Scope.mostSpecificMethodBinding(MethodBinding[], int, TypeBinding[], InvocationSite, ReferenceBinding): + * If choice between equivalent methods would otherwise be arbitrary, determine if m1 should be preferred due + * to a more specific null contract. + */ + public static boolean hasMoreSpecificNullness(MethodBinding m1, MethodBinding m2) { + long nullness1 = m1.returnType.tagBits & TagBits.AnnotationNullMASK; + long nullness2 = m2.returnType.tagBits & TagBits.AnnotationNullMASK; + if (nullness1 == TagBits.AnnotationNonNull && nullness2 != TagBits.AnnotationNonNull) + return true; + int len = Math.max(m1.parameters.length, m2.parameters.length); + for (int i=0; i TypeDeclaration.TESTING_GH_2158 = false; } } +public void testGH2325() { + Runner runner = new Runner(); + runner.customOptions = getCompilerOptions(); + runner.customOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.IGNORE); + runner.testFiles = new String[] { + "Sample.java", + """ + import org.eclipse.jdt.annotation.NonNull; + import org.eclipse.jdt.annotation.Nullable; + interface InterfaceA { + @Nullable Object get(); + } + interface InterfaceB { + @NonNull Object get(); + } + interface InterfaceAB extends InterfaceA, InterfaceB {} + interface InterfaceBA extends InterfaceB, InterfaceA {} + class Sample { + void ab(InterfaceAB ab) { + @NonNull Object obj = ab.get(); + // ^^^^^^^^ + // ⚠ Null type mismatch (type annotations): required '@NonNull Object' but this expression has type '@Nullable Object' + // Expected: no "Null type mismatch" problem, + // because the union of the two null constraints has to be @Nullable, the most restrictive null constraint + // (@Nullable violates the null constraint given by InterfaceB; @NonNull fulfills both null constraints from InterfaceA and InterfaceB) + } + void ba(InterfaceBA ba) { + @NonNull Object obj = ba.get(); // (no "Null type mismatch" as expected) + } + } + """ + }; + runner.classLibraries = this.LIBS; + runner.runConformTest(); +} }