Skip to content

Commit

Permalink
Merge branch 'master' into type-argument
Browse files Browse the repository at this point in the history
  • Loading branch information
Ao-senXiong authored Jan 29, 2025
2 parents f806c1d + e558998 commit c529f0d
Show file tree
Hide file tree
Showing 48 changed files with 662 additions and 169 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
uses: gradle/actions/setup-gradle@v4.2.2

- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.10.0
uses: bazel-contrib/setup-bazel@0.13.0
if: ${{ matrix.script == 'cftests-nonjunit' }}
with:
# Avoid downloading Bazel every time.
Expand Down Expand Up @@ -212,7 +212,7 @@ jobs:
texlive-font-utils texlive-fonts-recommended texlive-latex-recommended
pip install black flake8 html5validator
- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.10.0
uses: bazel-contrib/setup-bazel@0.13.0
if: ${{ matrix.script == 'cftests-nonjunit' }}
with:
# Avoid downloading Bazel every time.
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
// Code formatting; defines targets "spotlessApply" and "spotlessCheck".
// https://github.com/diffplug/spotless/tags ; see tags starting "gradle/"
// Only works on JDK 11+.
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.25.0'
classpath 'com.diffplug.spotless:spotless-plugin-gradle:7.0.2'
}
}
}
Expand Down Expand Up @@ -277,7 +277,7 @@ allprojects {
target '*.md', '*.tex', '.gitignore', 'Makefile'
targetExclude doNotFormat
// define the steps to apply to those files
indentWithSpaces(2)
leadingTabsToSpaces(2)
trimTrailingWhitespace()
// endWithNewline() // Don't want to end empty files with a newline
}
Expand Down Expand Up @@ -314,7 +314,7 @@ allprojects {
target '**/*.gradle'
targetExclude doNotFormat
greclipse() // which formatter Spotless should use to format .gradle files.
indentWithSpaces(4)
leadingTabsToSpaces(4)
trimTrailingWhitespace()
// endWithNewline() // Don't want to end empty files with a newline
}
Expand Down
4 changes: 4 additions & 0 deletions checker/bin-devel/test-misc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ PLUME_SCRIPTS="$SCRIPTDIR/.plume-scripts"
"$GIT_SCRIPTS/git-clone-related" eisop checker-framework.demos
./gradlew :checker:demosTests --console=plain --warning-mode=all

## Checker Framework templatefora-checker
"$GIT_SCRIPTS/git-clone-related" eisop templatefora-checker
./gradlew :checker:templateTests --console=plain --warning-mode=all

status=0

## Code style and formatting
Expand Down
37 changes: 37 additions & 0 deletions checker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,43 @@ task demosTests(dependsOn: assembleForJavac, group: 'Verification') {
}
}

task templateTests(dependsOn: assembleForJavac, group: 'Verification') {
description = 'Test that the templatefora-checker is working as expected.'

def injected = project.objects.newInstance(InjectedExecOps)

doLast {
File templateforCheckerDir = new File(projectDir, '../../templatefora-checker');
if (!templateforCheckerDir.exists()) {
injected.execOps.exec {
workingDir file(templateforCheckerDir.toString() + '/../')
executable 'git'
args = [
'clone',
'--depth',
'1',
'https://github.com/eisop/templatefora-checker.git'
]
}
} else {
injected.execOps.exec {
workingDir templateforCheckerDir
executable 'git'
args = [
'pull',
'https://github.com/eisop/templatefora-checker.git'
]
ignoreExitValue = true
}
}
println "Running Gradle build in $templateforCheckerDir"
injected.execOps.exec {
workingDir = templateforCheckerDir
commandLine "./gradlew", "build"
}
}
}

task allNullnessTests(type: Test, group: 'Verification') {
description = 'Run all JUnit tests for the Nullness Checker.'
include '**/Nullness*.class'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ public Void visitMethodInvocation(MethodInvocationTree tree, Void p) {
if (!isWrappedFormatCall(fc, enclosingMethod)) {
ftu.warning(invc, "format.indirect.arguments");
}
// TODO: If it is explict array construction, such as "new Object[] {
// ... }", then we could treat it like the VARARGS case, analyzing each
// argument. "new array" is probably rare, in the varargs position.
// fall through
// TODO: If it is explict array construction, such as "new Object[] {
// ... }", then we could treat it like the VARARGS case, analyzing each
// argument. "new array" is probably rare, in the varargs position.
// fall through
case NULLARRAY:
for (ConversionCategory cat : formatCats) {
if (cat == ConversionCategory.NULL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private void checkInvocationFormatFor(I18nFormatCall fc) {
}
break;
case NULLARRAY:
// fall-through
// fall-through
case ARRAY:
for (I18nConversionCategory cat : formatCats) {
if (cat == I18nConversionCategory.UNUSED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ protected DependentTypesHelper createDependentTypesHelper() {

/**
* The Substring Index qualifier hierarchy. The hierarchy consists of a top element {@link
* UNKNOWN} of type {@link SubstringIndexUnknown}, bottom element {@link BOTTOM} of type {@link
* SubstringIndexBottom}, and elements of type {@link SubstringIndexFor} that follow the
* #UNKNOWN} of type {@link SubstringIndexUnknown}, bottom element {@link #BOTTOM} of type
* {@link SubstringIndexBottom}, and elements of type {@link SubstringIndexFor} that follow the
* subtyping relation of {@link UBQualifier}.
*/
private final class SubstringIndexQualifierHierarchy extends ElementQualifierHierarchy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.checkerframework.javacutil.TreePathUtil;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TreeUtilsAfterJava11;
import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils;
import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils;
import org.checkerframework.javacutil.TypesUtils;

Expand Down Expand Up @@ -233,7 +234,7 @@ protected boolean commonAssignmentCheck(
|| !((IdentifierTree) receiver).getName().contentEquals("this")) {
return null;
}
// fallthrough
// fallthrough
case IDENTIFIER:
TreePath path = getCurrentPath();
if (TreePathUtil.inConstructor(path)) {
Expand Down Expand Up @@ -471,16 +472,31 @@ public Void visitInstanceOf(InstanceOfTree tree, Void p) {
// Handle them properly.
return null;
}

List<? extends AnnotationMirror> annotations = null;
if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) {
List<? extends AnnotationMirror> annotations =
TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree);
annotations = TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree);
} else {
Tree patternTree = TreeUtilsAfterJava11.InstanceOfUtils.getPattern(tree);
if (patternTree != null && TreeUtils.isBindingPatternTree(patternTree)) {
VariableTree variableTree = BindingPatternUtils.getVariable(patternTree);
if (variableTree.getModifiers() != null) {
List<? extends AnnotationTree> annotationTree =
variableTree.getModifiers().getAnnotations();
annotations = TreeUtils.annotationsFromTypeAnnotationTrees(annotationTree);
}
}
}

if (annotations != null) {
if (AnnotationUtils.containsSame(annotations, NULLABLE)) {
checker.reportError(tree, "instanceof.nullable");
}
if (AnnotationUtils.containsSame(annotations, NONNULL)) {
checker.reportWarning(tree, "instanceof.nonnull.redundant");
}
}

// Don't call super because it will issue an incorrect instanceof.unsafe warning.
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@ public boolean canCreateObligations() {

@Override
@SuppressWarnings("TypeParameterUnusedInFormals") // Intentional abuse
public <T extends GenericAnnotatedTypeFactory<?, ?, ?, ?>> @Nullable T getTypeFactoryOfSubcheckerOrNull(Class<? extends BaseTypeChecker> subCheckerClass) {
public <T extends GenericAnnotatedTypeFactory<?, ?, ?, ?>>
@Nullable T getTypeFactoryOfSubcheckerOrNull(
Class<? extends BaseTypeChecker> subCheckerClass) {
if (subCheckerClass == MustCallChecker.class) {
if (!canCreateObligations()) {
return super.getTypeFactoryOfSubcheckerOrNull(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ tree, getCurrentPath())) {
}
break;
}
// Other plus binary trees should be handled in the default case.
// fall through
// Other plus binary trees should be handled in the default case.
// fall through
default:
if (leftOpType.hasEffectiveAnnotation(Unsigned.class)
&& rightOpType.hasEffectiveAnnotation(Signed.class)) {
Expand Down Expand Up @@ -354,8 +354,8 @@ public Void visitCompoundAssignment(CompoundAssignmentTree tree, Void p) {
}
break;
}
// Other plus binary trees should be handled in the default case.
// fall through
// Other plus binary trees should be handled in the default case.
// fall through
default:
if (varType.hasAnnotation(Unsigned.class) && exprType.hasAnnotation(Signed.class)) {
checker.reportError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
public class TypeVarReturnAnnotated {

public static <T extends TypeVarReturnAnnotated>
@org.checkerframework.checker.initialization.qual.FBCBottom @org.checkerframework.checker.nullness.qual.Nullable T extract() {
@org.checkerframework.checker.initialization.qual.FBCBottom @org.checkerframework.checker.nullness.qual.Nullable T
extract() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ public java.lang.String toString() {

@org.checkerframework.dataflow.qual.SideEffectFree
@java.lang.SuppressWarnings("all")
public static CheckerFrameworkBuilder.@org.checkerframework.common.aliasing.qual.Unique CheckerFrameworkBuilderBuilder builder() {
public static CheckerFrameworkBuilder.@org.checkerframework.common.aliasing.qual.Unique CheckerFrameworkBuilderBuilder
builder() {
return new CheckerFrameworkBuilder.CheckerFrameworkBuilderBuilder();
}
}
4 changes: 2 additions & 2 deletions checker/tests/fenum/TestSwitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void m() {
int plain = 9; // FenumUnqualified

switch (plain) {
// :: error: (switch.type.incompatible)
// :: error: (switch.type.incompatible)
case annotated:
default:
}
Expand All @@ -24,7 +24,7 @@ void m() {
}

switch (annotated) {
// :: error: (switch.type.incompatible)
// :: error: (switch.type.incompatible)
case 45:
default:
}
Expand Down
134 changes: 134 additions & 0 deletions checker/tests/nullness/generics/WildcardBounds.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

class WildcardBounds {

abstract class OuterNbl<T extends @Nullable Object> {
abstract T get();

abstract class Inner<U extends T> {
abstract U get();

abstract class Chain<V extends U, W extends V> {
abstract W get();
}

Object m0(Chain<? extends Object, ? extends Object> p) {
return p.get();
}

Object m1(Chain<? extends @NonNull T, ? extends @NonNull T> p) {
return p.get();
}

Object m2(Chain<?, ?> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m3(Chain<? extends @Nullable Object, ? extends @Nullable Object> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m4(Chain<@NonNull ?, @NonNull ?> p) {
return p.get();
}

void callsNonNull(
OuterNbl<Object>.Inner<Number> i,
OuterNbl<Object>.Inner<Number>.Chain<Integer, Integer> n) {
i.m0(n);
i.m1(n);
i.m2(n);
i.m3(n);
i.m4(n);
}

void callsNullable(
OuterNbl<@Nullable Object>.Inner<@Nullable Number> i,
OuterNbl<@Nullable Object>.Inner<@Nullable Number>.Chain<
@Nullable Integer, @Nullable Integer>
n) {
// :: error: (argument.type.incompatible)
i.m0(n);
// :: error: (argument.type.incompatible)
i.m1(n);
// OK
i.m2(n);
// OK
i.m3(n);
// :: error: (argument.type.incompatible)
i.m4(n);
}
}

Object m0(Inner<? extends Object> p) {
return p.get();
}

Object m1(Inner<? extends @NonNull T> p) {
return p.get();
}

Object m2(Inner<?> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m3(Inner<? extends @Nullable Object> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m4(Inner<@NonNull ?> p) {
return p.get();
}

// We could add calls for these methods.
}

Object m0(OuterNbl<? extends Object> p) {
return p.get();
}

Object m1(OuterNbl<? extends @NonNull Object> p) {
return p.get();
}

Object m2(OuterNbl<?> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m3(OuterNbl<? extends @Nullable Object> p) {
// :: error: (return.type.incompatible)
return p.get();
}

Object m4(OuterNbl<@NonNull ?> p) {
return p.get();
}

void callsOuter(OuterNbl<String> s, OuterNbl<@Nullable String> ns) {
m0(s);
m1(s);
m2(s);
m3(s);
m4(s);

// :: error: (argument.type.incompatible)
m0(ns);
// :: error: (argument.type.incompatible)
m1(ns);
// OK
m2(ns);
// OK
m3(ns);
// :: error: (argument.type.incompatible)
m4(ns);
}

// We could add an OuterNonNull to also test with a non-null upper bound.
// But we probably already test that enough.
}
Loading

0 comments on commit c529f0d

Please sign in to comment.