Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.checkerframework.framework.test.junit;

import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest;
import org.checkerframework.framework.testchecker.util.EvenOddChecker;
import org.junit.Ignore;
import org.junit.runners.Parameterized.Parameters;

import java.io.File;
import java.util.List;

/**
* Disabled regression test for per-bound annotation preservation on intersection-type upper bounds.
*
* <p>An intersection-type bound such as {@code <T extends @Odd IfaceA & IfaceB>} homogenizes its
* per-bound annotations: {@code AnnotatedIntersectionType.copyIntersectionBoundAnnotations} feeds
* the summarized primary annotation through {@code addAnnotations}, whose intersection override
* calls {@code fixupBoundAnnotations} and copies that primary back onto every bound. The
* unannotated bound therefore loses its default (top) qualifier and appears to carry the other
* bound's qualifier, order-dependently. The test inputs in {@code
* tests/disabled-intersection-bounds} assert the fixed, per-bound behavior.
*
* <p>This test is {@link Ignore}d because the narrowest fix (not writing the primary back onto the
* bounds) changes intersection-bound defaulting and subtyping semantics that the standard Nullness
* Checker tests deliberately rely on (see {@code checker/tests/nullness/Issue868.java} and {@code
* Issue3349.java}), so it is out of scope for a contained bug fix. See {@code
* cf-tasks/task-2-findings.md} for the full diagnosis and proposed patch.
*/
@Ignore("Fix changes intersection-bound semantics relied on by the Nullness Checker; see findings.")
public class DisabledIntersectionBoundAnnosTest extends CheckerFrameworkPerDirectoryTest {

/**
* Creates a new DisabledIntersectionBoundAnnosTest.
*
* @param testFiles the files containing test code, which will be type-checked
*/
public DisabledIntersectionBoundAnnosTest(List<File> testFiles) {
super(testFiles, EvenOddChecker.class, "disabled-intersection-bounds");
}

/**
* Returns the directories containing test code.
*
* @return the directories containing test code
*/
@Parameters
public static String[] getTestDirs() {
return new String[] {"disabled-intersection-bounds"};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import org.checkerframework.framework.testchecker.util.Odd;

// DISABLED regression test: run by DisabledIntersectionBoundAnnosTest, which is
// @Ignore'd. The fix it verifies changes intersection-bound defaulting/subtyping
// semantics that the standard Nullness Checker tests deliberately rely on, so it
// is out of scope for a contained bug fix. See cf-tasks/task-2-findings.md.
//
// Regression test for per-bound annotation preservation on intersection-type
// upper bounds. The unannotated bound must retain its default (top) qualifier
// instead of being homogenized to the other bound's explicit @Odd. Both bounds
// are interfaces so the two declarations can differ only in bound order.
// See AnnotatedIntersectionType.copyIntersectionBoundAnnotations.
public class IntersectionBoundAnnos {

interface IfaceA {}

interface IfaceB {}

<T extends @Odd IfaceA & IfaceB> void oddFirst(T t) {
// The IfaceA bound is @Odd; t is assignable to @Odd IfaceA.
@Odd IfaceA ok = t;
// IfaceB is unannotated, so t viewed as IfaceB is top, not @Odd. Before
// the fix this spuriously type-checked because the IfaceB bound was
// homogenized to the other bound's @Odd.
// :: error: (assignment.type.incompatible)
@Odd IfaceB bad = t;
}

// Order-dependence guard: the @Odd bound comes second. The unannotated IfaceB
// bound must still default to top, independent of the bound order and of the
// order in which methods (and thus type parameters) are processed.
<T extends IfaceB & @Odd IfaceA> void oddSecond(T t) {
@Odd IfaceA ok = t;
// :: error: (assignment.type.incompatible)
@Odd IfaceB bad = t;
}
}
Loading