From 9bdb6ac997fdc0f7c2caf56af400987d7316a0e0 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Fri, 8 Sep 2023 13:47:34 -0700 Subject: [PATCH 01/91] Update for Java 21 --- azure-pipelines.yml | 199 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 157 insertions(+), 42 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e3670044adc..31aa6c582a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,19 +11,20 @@ pr: jobs: # The dependsOn clauses are: -# * Everything depends on the canary jobs (the 4 main jdk17 jobs), except those jobs themselves. -# * Anything *_jdk11 or *_jdk20 depends on *_jdk17. +# * Everything depends on the canary jobs (the main jdk21 jobs), except those jobs themselves. +# * Anything *_jdk11 or *_jdk17 or *_jdk20 depends on *_jdk21. +## TODO: The jdk20 jobs are a bit gratuitous. They are placeholders for jdk22 when it is released. # misc_jdk20 because JDK 20 adds more strict checking (e.g., Javadoc) - job: canary_jobs dependsOn: - - junit_jdk17 - - nonjunit_jdk17 - - inference_part1_jdk17 - - inference_part2_jdk17 - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 - - misc_jdk17 + - junit_jdk21 + - nonjunit_jdk21 + - inference_part1_jdk21 + - inference_part2_jdk21 + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 + - misc_jdk21 - misc_jdk20 pool: vmImage: 'ubuntu-latest' @@ -34,7 +35,7 @@ jobs: - job: junit_jdk11 dependsOn: - canary_jobs - - junit_jdk17 + - junit_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -44,9 +45,21 @@ jobs: - bash: ./checker/bin-devel/test-cftests-junit.sh displayName: test-cftests-junit.sh - job: junit_jdk17 + dependsOn: + - canary_jobs + - junit_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-cftests-junit.sh + displayName: test-cftests-junit.sh +- job: junit_jdk21 + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 70 steps: - checkout: self @@ -68,7 +81,7 @@ jobs: - job: nonjunit_jdk11 dependsOn: - canary_jobs - - nonjunit_jdk17 + - nonjunit_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -78,6 +91,9 @@ jobs: - bash: ./checker/bin-devel/test-cftests-nonjunit.sh displayName: test-cftests-nonjunit.sh - job: nonjunit_jdk17 + dependsOn: + - canary_jobs + - nonjunit_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest @@ -86,6 +102,15 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-nonjunit.sh displayName: test-cftests-nonjunit.sh +- job: nonjunit_jdk21 + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-cftests-nonjunit.sh + displayName: test-cftests-nonjunit.sh - job: nonjunit_jdk20 dependsOn: - canary_jobs @@ -104,8 +129,8 @@ jobs: - job: inference_jdk11 dependsOn: - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_part1_jdk21 + - inference_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -115,21 +140,35 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-inference.sh displayName: test-cftests-inference.sh -# Split into part1 and part2 only for the inference job that "canary_jobs" depends on. -- job: inference_part1_jdk17 +- job: inference_jdk17 + dependsOn: + - canary_jobs + - inference_part1_jdk21 + - inference_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest timeoutInMinutes: 90 steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-cftests-inference.sh + displayName: test-cftests-inference.sh +# Split into part1 and part2 only for the inference job that "canary_jobs" depends on. +- job: inference_part1_jdk21 + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest + timeoutInMinutes: 90 + steps: - checkout: self fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-inference-part1.sh displayName: test-cftests-inference-part1.sh -- job: inference_part2_jdk17 +- job: inference_part2_jdk21 pool: vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk17:latest + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 90 steps: - checkout: self @@ -139,8 +178,8 @@ jobs: - job: inference_jdk20 dependsOn: - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_part1_jdk21 + - inference_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20:latest @@ -154,7 +193,7 @@ jobs: - job: misc_jdk11 dependsOn: - canary_jobs - - misc_jdk17 + - misc_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11-plus:latest @@ -164,6 +203,9 @@ jobs: - bash: ./checker/bin-devel/test-misc.sh displayName: test-misc.sh - job: misc_jdk17 + dependsOn: + - canary_jobs + - misc_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17-plus:latest @@ -172,10 +214,19 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-misc.sh displayName: test-misc.sh +- job: misc_jdk21 + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21-plus:latest + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-misc.sh + displayName: test-misc.sh - job: misc_jdk20 dependsOn: # - canary_jobs - - misc_jdk17 + - misc_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20-plus:latest @@ -187,8 +238,8 @@ jobs: - job: typecheck_jdk11 dependsOn: - canary_jobs - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11-plus:latest @@ -197,20 +248,33 @@ jobs: fetchDepth: 1000 - bash: ./checker/bin-devel/test-typecheck.sh displayName: test-typecheck.sh -# Split into part1 and part2 only for the type-checking job that "canary_jobs" depends on. -- job: typecheck_part1_jdk17 +- job: typecheck_jdk17 + dependsOn: + - canary_jobs + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17-plus:latest steps: + - checkout: self + fetchDepth: 1000 + - bash: ./checker/bin-devel/test-typecheck.sh + displayName: test-typecheck.sh +# Split into part1 and part2 only for the type-checking job that "canary_jobs" depends on. +- job: typecheck_part1_jdk21 + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21-plus:latest + steps: - checkout: self fetchDepth: 1000 - bash: ./checker/bin-devel/test-typecheck-part1.sh displayName: test-typecheck-part1.sh -- job: typecheck_part2_jdk17 +- job: typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk17-plus:latest + container: mdernst/cf-ubuntu-jdk21-plus:latest steps: - checkout: self fetchDepth: 1000 @@ -219,8 +283,8 @@ jobs: - job: typecheck_jdk20 dependsOn: - canary_jobs - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20-plus:latest @@ -232,8 +296,8 @@ jobs: - job: daikon_jdk11 dependsOn: - canary_jobs - - daikon_part1_jdk17 - - daikon_part2_jdk17 + - daikon_part1_jdk21 + - daikon_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -243,24 +307,38 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-daikon.sh displayName: test-daikon.sh -- job: daikon_part1_jdk17 +- job: daikon_jdk17 dependsOn: - canary_jobs + - daikon_part1_jdk21 + - daikon_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest + timeoutInMinutes: 80 + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-daikon.sh + displayName: test-daikon.sh +- job: daikon_part1_jdk21 + dependsOn: + - canary_jobs + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 70 steps: - checkout: self fetchDepth: 25 - bash: ./checker/bin-devel/test-daikon-part1.sh displayName: test-daikon.sh -- job: daikon_part2_jdk17 +- job: daikon_part2_jdk21 dependsOn: - canary_jobs pool: vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk17:latest + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 80 steps: - checkout: self @@ -270,8 +348,8 @@ jobs: - job: daikon_jdk20 dependsOn: - canary_jobs - - daikon_part1_jdk17 - - daikon_part2_jdk17 + - daikon_part1_jdk21 + - daikon_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20:latest @@ -284,7 +362,7 @@ jobs: - job: guava_jdk11 dependsOn: - canary_jobs - - guava_jdk17 + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -297,9 +375,22 @@ jobs: - job: guava_jdk17 dependsOn: - canary_jobs + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest + timeoutInMinutes: 60 + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-guava.sh + displayName: test-guava.sh +- job: guava_jdk21 + dependsOn: + - canary_jobs + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest # The guava job sometimes times out, because the time between these lines can be 30 minutes! # [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugin-tools/maven-plugin-tools-generators/3.5.1/maven-plugin-tools-generators-3.5.1.pom # [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugin-tools/maven-plugin-tools-generators/3.5.1/maven-plugin-tools-generators-3.5.1.pom @@ -314,7 +405,7 @@ jobs: - job: guava_jdk20 dependsOn: - canary_jobs - - guava_jdk17 + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20:latest @@ -327,7 +418,7 @@ jobs: - job: plume_lib_jdk11 dependsOn: - canary_jobs - - plume_lib_jdk17 + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk11:latest @@ -339,6 +430,7 @@ jobs: - job: plume_lib_jdk17 dependsOn: - canary_jobs + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk17:latest @@ -347,10 +439,21 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-plume-lib.sh displayName: test-plume-lib.sh +- job: plume_lib_jdk21 + dependsOn: + - canary_jobs + pool: + vmImage: 'ubuntu-latest' + container: mdernst/cf-ubuntu-jdk21:latest + steps: + - checkout: self + fetchDepth: 25 + - bash: ./checker/bin-devel/test-plume-lib.sh + displayName: test-plume-lib.sh - job: plume_lib_jdk20 dependsOn: - canary_jobs - - plume_lib_jdk17 + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: mdernst/cf-ubuntu-jdk20:latest @@ -363,7 +466,7 @@ jobs: # - job: downstream_jdk11 # dependsOn: # - canary_jobs -# - downstream_jdk17 +# - downstream_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: mdernst/cf-ubuntu-jdk11:latest @@ -375,6 +478,7 @@ jobs: # - job: downstream_jdk17 # dependsOn: # - canary_jobs +# - downstream_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: mdernst/cf-ubuntu-jdk17:latest @@ -383,10 +487,21 @@ jobs: # fetchDepth: 25 # - bash: ./checker/bin-devel/test-downstream.sh # displayName: test-downstream.sh +# - job: downstream_jdk21 +# dependsOn: +# - canary_jobs +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk21:latest +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-downstream.sh +# displayName: test-downstream.sh # - job: downstream_jdk20 # dependsOn: # - canary_jobs -# - downstream_jdk17 +# - downstream_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: mdernst/cf-ubuntu-jdk20:latest From 3e46273e67a563541bda62dae159863802a6985b Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 9 Sep 2023 08:44:40 -0700 Subject: [PATCH 02/91] Test under Java 21 --- build.gradle | 4 ++-- checker/bin-devel/Dockerfile-README | 4 ++-- checker/bin-devel/Dockerfile-ubuntu-jdk11 | 2 ++ checker/bin-devel/Dockerfile-ubuntu-jdk17 | 2 ++ .../bin-devel/Dockerfile-ubuntu-jdk17-plus | 2 -- ...e-ubuntu-jdk20 => Dockerfile-ubuntu-jdk21} | 12 ++++------ ...dk20-plus => Dockerfile-ubuntu-jdk21-plus} | 12 ++++------ checker/bin/wpi-many.sh | 24 +++++++++---------- checker/bin/wpi.sh | 24 +++++++++---------- docs/CHANGELOG.md | 4 +++- docs/manual/external-tools.tex | 4 ++-- docs/manual/inference.tex | 6 ++--- 12 files changed, 49 insertions(+), 51 deletions(-) rename checker/bin-devel/{Dockerfile-ubuntu-jdk20 => Dockerfile-ubuntu-jdk21} (87%) rename checker/bin-devel/{Dockerfile-ubuntu-jdk20-plus => Dockerfile-ubuntu-jdk21-plus} (91%) diff --git a/build.gradle b/build.gradle index fdd721e63a3..9e8b066ef1a 100644 --- a/build.gradle +++ b/build.gradle @@ -33,8 +33,8 @@ ext { // isJava17compatible = JavaVersion.isCompatibleWith(JavaVersion.VERSION_17) isJava17orHigher = JavaVersion.current() >= JavaVersion.VERSION_17 - // As of 2022-04-22, delombok doesn't yet support JDK 20; see https://projectlombok.org/changelog . - skipDelombok = JavaVersion.current() == JavaVersion.VERSION_20 + // As of 2023-09-08, delombok doesn't yet support JDK 22; see https://projectlombok.org/changelog . + skipDelombok = JavaVersion.current() == JavaVersion.VERSION_22 parentDir = file("${rootDir}/../").absolutePath diff --git a/checker/bin-devel/Dockerfile-README b/checker/bin-devel/Dockerfile-README index aa6b5e4daac..7b4a5c7c5e5 100644 --- a/checker/bin-devel/Dockerfile-README +++ b/checker/bin-devel/Dockerfile-README @@ -52,12 +52,12 @@ export PROJECT=cf create_upload_docker_image export OS=ubuntu -export JDKVER=jdk20 +export JDKVER=jdk21 export PROJECT=cf create_upload_docker_image export OS=ubuntu -export JDKVER=jdk20-plus +export JDKVER=jdk21-plus export PROJECT=cf create_upload_docker_image diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11 b/checker/bin-devel/Dockerfile-ubuntu-jdk11 index d498347114d..4c3ccef7d26 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11 @@ -21,6 +21,8 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ openjdk-11-jdk +# Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . + RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk17 b/checker/bin-devel/Dockerfile-ubuntu-jdk17 index 51967ce3de1..694f4622250 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk17 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk17 @@ -21,6 +21,8 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ openjdk-17-jdk +# Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . + RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus index d321b9e26bf..87ce7b241e9 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus @@ -5,8 +5,6 @@ FROM ubuntu MAINTAINER Michael Ernst -## Keep this file in sync with ../../docs/manual/troubleshooting.tex - # According to # https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/: # * Put "apt-get update" and "apt-get install" and "apt cleanup" in the same RUN command. diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20 b/checker/bin-devel/Dockerfile-ubuntu-jdk21 similarity index 87% rename from checker/bin-devel/Dockerfile-ubuntu-jdk20 rename to checker/bin-devel/Dockerfile-ubuntu-jdk21 index cc53cc3c7db..40114f0195c 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk20 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21 @@ -1,9 +1,9 @@ # Create a Docker image that is ready to run the main Checker Framework tests, -# using JDK 20. +# using JDK 21. # (This is OpenJDK, not Oracle JDK. There are different instructions for # installing a LTS release of Java.) # To convert this file to use a newer JDK, search (from the top level of the -# Checker Framework and Annotation Tools repositories) for: (java|jdk).?20 +# Checker Framework and Annotation Tools repositories) for: (java|jdk).?21\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. FROM ubuntu:rolling @@ -20,14 +20,10 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ apt-utils -# ca-certificates-java is a dependency of openjdk-20-jdk, but the installation -# process seems to fail sometimes when only openjdk-20-jdk is specified. RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ - ca-certificates-java \ -&& aptitude -y install \ - openjdk-20-jdk + openjdk-21-jdk # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . @@ -49,7 +45,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ jtreg6 -# Maven 3.8.7 is the default on Ubuntu 23.04. +# Maven 3.8.7 is the default on Ubuntu 23.04, so the below is not needed. # (Don't try to use a variable here for the Maven version.) # RUN export DEBIAN_FRONTEND=noninteractive \ # && wget https://mirrors.sonic.net/apache/maven/maven-3/3.9.2/binaries/apache-maven-3.9.2-bin.tar.gz \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus similarity index 91% rename from checker/bin-devel/Dockerfile-ubuntu-jdk20-plus rename to checker/bin-devel/Dockerfile-ubuntu-jdk21-plus index 3e66053a526..2fef32556b2 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus @@ -1,14 +1,16 @@ # Create a Docker image that is ready to run the full Checker Framework tests, -# including building the manual and Javadoc, using JDK 20. +# including building the manual and Javadoc, using JDK 21. # (This is OpenJDK, not Oracle JDK. There are different instructions for # installing a LTS release of Java.) # To convert this file to use a newer JDK, search (from the top level of the -# Checker Framework and Annotation Tools repositories) for: (java|jdk).?20 +# Checker Framework and Annotation Tools repositories) for: (java|jdk).?21\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. FROM ubuntu:rolling MAINTAINER Michael Ernst +## Keep this file in sync with ../../docs/manual/troubleshooting.tex + # According to # https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/: # * Put "apt-get update" and "apt-get install" and "apt cleanup" in the same RUN command. @@ -20,14 +22,10 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ apt-utils -# ca-certificates-java is a dependency of openjdk-20-jdk, but the installation -# process seems to fail sometimes when only openjdk-20-jdk is specified. RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ - ca-certificates-java \ -&& aptitude -y install \ - openjdk-20-jdk + openjdk-21-jdk # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin/wpi-many.sh b/checker/bin/wpi-many.sh index bf63494b9d9..f8eaf21a50b 100755 --- a/checker/bin/wpi-many.sh +++ b/checker/bin/wpi-many.sh @@ -73,11 +73,11 @@ else has_java17="yes" fi -# shellcheck disable=SC2153 # testing for JAVA20_HOME, not a typo of JAVA_HOME -if [ "${JAVA20_HOME}" = "" ]; then - has_java20="no" +# shellcheck disable=SC2153 # testing for JAVA21_HOME, not a typo of JAVA_HOME +if [ "${JAVA21_HOME}" = "" ]; then + has_java21="no" else - has_java20="yes" + has_java21="yes" fi if [ "${has_java_home}" = "yes" ] && [ ! -d "${JAVA_HOME}" ]; then @@ -99,9 +99,9 @@ if [ "${has_java_home}" = "yes" ]; then export JAVA17_HOME="${JAVA_HOME}" has_java17="yes" fi - if [ "${has_java20}" = "no" ] && [ "${java_version}" = 20 ]; then - export JAVA20_HOME="${JAVA_HOME}" - has_java20="yes" + if [ "${has_java21}" = "no" ] && [ "${java_version}" = 21 ]; then + export JAVA21_HOME="${JAVA_HOME}" + has_java21="yes" fi fi @@ -120,22 +120,22 @@ if [ "${has_java17}" = "yes" ] && [ ! -d "${JAVA17_HOME}" ]; then exit 1 fi -if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA20_HOME}" ]; then - echo "JAVA20_HOME is set to a non-existent directory ${JAVA20_HOME}" +if [ "${has_java21}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then + echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" exit 1 fi -if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java20}" = "no" ]; then +if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java21}" = "no" ]; then if [ "${has_java_home}" = "yes" ]; then echo "Cannot determine Java version from JAVA_HOME" else - echo "No Java 8, 11, 17, or 20 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA20_HOME must be set." + echo "No Java 8, 11, 17, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." fi echo "JAVA_HOME = ${JAVA_HOME}" echo "JAVA8_HOME = ${JAVA8_HOME}" echo "JAVA11_HOME = ${JAVA11_HOME}" echo "JAVA17_HOME = ${JAVA17_HOME}" - echo "JAVA20_HOME = ${JAVA20_HOME}" + echo "JAVA21_HOME = ${JAVA21_HOME}" command -v java java -version exit 1 diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 216e98588f3..2aec1b8a76d 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -68,11 +68,11 @@ else has_java17="yes" fi -# shellcheck disable=SC2153 # testing for JAVA20_HOME, not a typo of JAVA_HOME -if [ "${JAVA20_HOME}" = "" ]; then - has_java20="no" +# shellcheck disable=SC2153 # testing for JAVA21_HOME, not a typo of JAVA_HOME +if [ "${JAVA21_HOME}" = "" ]; then + has_java21="no" else - has_java20="yes" + has_java21="yes" fi if [ "${has_java_home}" = "yes" ] && [ ! -d "${JAVA_HOME}" ]; then @@ -94,9 +94,9 @@ if [ "${has_java_home}" = "yes" ]; then export JAVA17_HOME="${JAVA_HOME}" has_java17="yes" fi - if [ "${has_java20}" = "no" ] && [ "${java_version}" = 20 ]; then - export JAVA20_HOME="${JAVA_HOME}" - has_java20="yes" + if [ "${has_java21}" = "no" ] && [ "${java_version}" = 21 ]; then + export JAVA21_HOME="${JAVA_HOME}" + has_java21="yes" fi fi @@ -115,22 +115,22 @@ if [ "${has_java17}" = "yes" ] && [ ! -d "${JAVA17_HOME}" ]; then exit 7 fi -if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA20_HOME}" ]; then - echo "JAVA20_HOME is set to a non-existent directory ${JAVA20_HOME}" +if [ "${has_java21}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then + echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" exit 7 fi -if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java20}" = "no" ]; then +if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java21}" = "no" ]; then if [ "${has_java_home}" = "yes" ]; then echo "Cannot determine Java version from JAVA_HOME" else - echo "No Java 8, 11, 17, or 20 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA20_HOME must be set." + echo "No Java 8, 11, 17, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." fi echo "JAVA_HOME = ${JAVA_HOME}" echo "JAVA8_HOME = ${JAVA8_HOME}" echo "JAVA11_HOME = ${JAVA11_HOME}" echo "JAVA17_HOME = ${JAVA17_HOME}" - echo "JAVA20_HOME = ${JAVA20_HOME}" + echo "JAVA21_HOME = ${JAVA21_HOME}" command -v java java -version exit 8 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d6af5d53e14..2f76e1eead5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,8 +1,10 @@ Version 3.38.1 (October 1, 2023) ----------------------------------- +-------------------------------- **User-visible changes:** +The Checker Framework runs under JDK 21 -- that is, it runs on a version 21 JVM. + **Implementation details:** **Closed issues:** diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 80ec5a093e8..15f5a1401d3 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -629,9 +629,9 @@ This section shows what arguments to pass to the OpenJDK or OracleJDK \ (\emph{not} the \ wrapper script of Section~\ref{javac-wrapper}) to run the Checker -Framework, if you are using JDK 9 or later. These +Framework. These instructions should work on JDK 9 or later, but we only test with supported -versions of Java: JDK 11, JDK 17, and JDK 20. +versions of Java: JDK 11, JDK 17, and JDK 21. \subsubsectionAndLabel{Non-modularized code}{javac-jdk11-non-modularized} diff --git a/docs/manual/inference.tex b/docs/manual/inference.tex index 6f401347d04..edede486caa 100644 --- a/docs/manual/inference.tex +++ b/docs/manual/inference.tex @@ -238,14 +238,14 @@ \item The project on which inference is run must contain an Ant, Gradle, or Maven buildfile that compiles the project. \item At least one of the \verb|JAVA_HOME|, - \verb|JAVA8_HOME|, \verb|JAVA11_HOME|, \verb|JAVA17_HOME|, or \verb|JAVA20_HOME| + \verb|JAVA8_HOME|, \verb|JAVA11_HOME|, \verb|JAVA17_HOME|, or \verb|JAVA21_HOME| environment variables must be set. \item If set, the \verb|JAVA_HOME| environment variable must point to a - Java 8, 11, 17, or 20 JDK. + Java 8, 11, 17, or 21 JDK. \item If set, the \verb|JAVA8_HOME| environment variable must point to a Java 8 JDK. \item If set, the \verb|JAVA11_HOME| environment variable must point to a Java 11 JDK. \item If set, the \verb|JAVA17_HOME| environment variable must point to a Java 17 JDK. -\item If set, the \verb|JAVA20_HOME| environment variable must point to a Java 20 JDK. +\item If set, the \verb|JAVA21_HOME| environment variable must point to a Java 21 JDK. \item \ environment variable must point to a built copy of the Checker Framework. \item If set, the \verb|DLJC| environment variable must point to a copy of the \ script from \ahreforurl{https://github.com/kelloggm/do-like-javac}{do-like-javac}. (If this variable is not From 58a2617885ad4d32cfa615862d6449aeb3cdfae4 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 9 Sep 2023 08:55:52 -0700 Subject: [PATCH 03/91] Lombok doesn't support JDK 21 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9e8b066ef1a..195d2a0a6fc 100644 --- a/build.gradle +++ b/build.gradle @@ -33,8 +33,8 @@ ext { // isJava17compatible = JavaVersion.isCompatibleWith(JavaVersion.VERSION_17) isJava17orHigher = JavaVersion.current() >= JavaVersion.VERSION_17 - // As of 2023-09-08, delombok doesn't yet support JDK 22; see https://projectlombok.org/changelog . - skipDelombok = JavaVersion.current() == JavaVersion.VERSION_22 + // As of 2023-09-08, delombok doesn't yet support JDK 21; see https://projectlombok.org/changelog . + skipDelombok = JavaVersion.current() == JavaVersion.VERSION_21 parentDir = file("${rootDir}/../").absolutePath From c5d2710514f9e3707b87e09c658130cf45bc1b90 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 9 Sep 2023 14:04:17 -0700 Subject: [PATCH 04/91] Lombok doesn't support JDK 21 --- docs/examples/lombok/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/examples/lombok/Makefile b/docs/examples/lombok/Makefile index 1cbd3243fc6..4896e8a4734 100644 --- a/docs/examples/lombok/Makefile +++ b/docs/examples/lombok/Makefile @@ -4,12 +4,18 @@ JAVA_VER := $(shell java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s/ # Delomboking seems to mess up line numbers. The actual error is on line 13, but the error appears on line 12. # So check for both the error message and make sure it is for the right assignment. +# As of 2020-09-09 lombok does not work under Java 21, see https://projectlombok.org/changelog . +ifeq (${JAVA_VER},21) +all: + @echo "Skipping test because lombok does not work under Java ${JAVA_VER}" +else all: clean - ../../../gradlew build > Out.txt 2>&1 (grep -qF "User.java:9: error: [argument] incompatible argument for parameter y of FooBuilder.y." Out.txt \ && grep -qF "Foo.java:12: error: [assignment] incompatible types in assignment." Out.txt \ && grep -qF "y = null; // error" Out.txt) \ || (echo "===== start of Out.txt =====" && cat Out.txt && echo "===== end of Out.txt =====" && false) +endif clean: ../../../gradlew clean From eebb32ab2fed1907e99191c30f087e46025118dc Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 9 Sep 2023 21:48:59 -0700 Subject: [PATCH 05/91] Clean WPI output --- checker/bin-devel/wpi-plumelib/bcel-util.expected | 1 - checker/bin-devel/wpi-plumelib/bibtex-clean.expected | 1 - checker/bin-devel/wpi-plumelib/html-pretty-print.expected | 1 - checker/bin-devel/wpi-plumelib/icalavailable.expected | 1 - checker/bin-devel/wpi-plumelib/lookup.expected | 1 - checker/bin-devel/wpi-plumelib/options.expected | 1 - checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh | 1 + 7 files changed, 1 insertion(+), 6 deletions(-) diff --git a/checker/bin-devel/wpi-plumelib/bcel-util.expected b/checker/bin-devel/wpi-plumelib/bcel-util.expected index 6a865c1e2f3..51a8d83cd66 100644 --- a/checker/bin-devel/wpi-plumelib/bcel-util.expected +++ b/checker/bin-devel/wpi-plumelib/bcel-util.expected @@ -1,4 +1,3 @@ -warning: [options] system modules path not set in conjunction with -source 11 BcelUtil.java:766: error: [argument] incompatible argument for parameter typename of ClassnameAndDimensions.parseFqBinaryName. Signatures.ClassnameAndDimensions.parseFqBinaryName(classname); ^ diff --git a/checker/bin-devel/wpi-plumelib/bibtex-clean.expected b/checker/bin-devel/wpi-plumelib/bibtex-clean.expected index 4fbf3a7809c..e69de29bb2d 100644 --- a/checker/bin-devel/wpi-plumelib/bibtex-clean.expected +++ b/checker/bin-devel/wpi-plumelib/bibtex-clean.expected @@ -1 +0,0 @@ -warning: [options] system modules path not set in conjunction with -source 11 diff --git a/checker/bin-devel/wpi-plumelib/html-pretty-print.expected b/checker/bin-devel/wpi-plumelib/html-pretty-print.expected index 4fbf3a7809c..e69de29bb2d 100644 --- a/checker/bin-devel/wpi-plumelib/html-pretty-print.expected +++ b/checker/bin-devel/wpi-plumelib/html-pretty-print.expected @@ -1 +0,0 @@ -warning: [options] system modules path not set in conjunction with -source 11 diff --git a/checker/bin-devel/wpi-plumelib/icalavailable.expected b/checker/bin-devel/wpi-plumelib/icalavailable.expected index 4fbf3a7809c..e69de29bb2d 100644 --- a/checker/bin-devel/wpi-plumelib/icalavailable.expected +++ b/checker/bin-devel/wpi-plumelib/icalavailable.expected @@ -1 +0,0 @@ -warning: [options] system modules path not set in conjunction with -source 11 diff --git a/checker/bin-devel/wpi-plumelib/lookup.expected b/checker/bin-devel/wpi-plumelib/lookup.expected index 4fbf3a7809c..e69de29bb2d 100644 --- a/checker/bin-devel/wpi-plumelib/lookup.expected +++ b/checker/bin-devel/wpi-plumelib/lookup.expected @@ -1 +0,0 @@ -warning: [options] system modules path not set in conjunction with -source 11 diff --git a/checker/bin-devel/wpi-plumelib/options.expected b/checker/bin-devel/wpi-plumelib/options.expected index 4fbf3a7809c..e69de29bb2d 100644 --- a/checker/bin-devel/wpi-plumelib/options.expected +++ b/checker/bin-devel/wpi-plumelib/options.expected @@ -1 +0,0 @@ -warning: [options] system modules path not set in conjunction with -source 11 diff --git a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh index 605b07b86f2..d35280c46f8 100755 --- a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh +++ b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh @@ -47,6 +47,7 @@ clean_compile_output() { sed -i '/^warning: \[path\] bad path element /d' "$out" sed -i '/^.*warning: Option --illegal-access is deprecated and will be removed in a future release./d' "$out" sed -i '/^warning: \[options\] bootstrap class path not set/d' "$out" + sed -i '/^warning: [options] system modules path not set in conjunction with -source 11/d' "$out" # Remove warning count because it can differ between JDK 8 and later JDKs due to the bootstrap warning: sed -i '/^[0-9]* warning/d' "$out" From 74d17178a598815746d0472c7c01d2dece4e2b8c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 10 Sep 2023 08:09:36 -0700 Subject: [PATCH 06/91] Add quoting --- checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh index d35280c46f8..e57fa9a87c1 100755 --- a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh +++ b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh @@ -47,7 +47,7 @@ clean_compile_output() { sed -i '/^warning: \[path\] bad path element /d' "$out" sed -i '/^.*warning: Option --illegal-access is deprecated and will be removed in a future release./d' "$out" sed -i '/^warning: \[options\] bootstrap class path not set/d' "$out" - sed -i '/^warning: [options] system modules path not set in conjunction with -source 11/d' "$out" + sed -i '/^warning: \[options\] system modules path not set in conjunction with -source 11/d' "$out" # Remove warning count because it can differ between JDK 8 and later JDKs due to the bootstrap warning: sed -i '/^[0-9]* warning/d' "$out" From de524ea361699585db437954c7b31680c44beafb Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 11 Sep 2023 00:20:19 -0700 Subject: [PATCH 07/91] WPI documentation --- checker/bin-devel/wpi-plumelib/README | 14 +++++++------- checker/tests/wpi-many/README.md | 4 ++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/checker/bin-devel/wpi-plumelib/README b/checker/bin-devel/wpi-plumelib/README index 894ca2830b0..2a766e285b6 100644 --- a/checker/bin-devel/wpi-plumelib/README +++ b/checker/bin-devel/wpi-plumelib/README @@ -11,13 +11,13 @@ running wpi.sh, with two permitted additions: The script `test-wpi-plumelib.sh` complements the Gradle `wpiManyTest` task. Here are differences: - * different projects use a different list of type-checkers. - (wpi-many.sh uses a fixed set of type-checkers for all projects) - * this uses the HEAD commit - (wpi-many.sh uses a fixed commit) - * this checks for expected type-checking errors - (the Gradle wpiManyTest target requires that there are no errors, so it - skips type systems for which inference does not currently work) + * Different projects use a different list of type-checkers. + (wpi-many.sh uses a fixed set of type-checkers for all projects.) + * This uses the HEAD commit. + (wpi-many.sh uses a fixed commit.) + * This checks for expected type-checking errors. + (The Gradle wpiManyTest target requires that there are no errors, so it + skips type systems for which inference does not currently work.) The use of the HEAD commit makes these tests brittle. They might fail if: * The Checker Framework is changed in a way that makes whole-program diff --git a/checker/tests/wpi-many/README.md b/checker/tests/wpi-many/README.md index fadd9b85945..9c2c9789b10 100644 --- a/checker/tests/wpi-many/README.md +++ b/checker/tests/wpi-many/README.md @@ -6,7 +6,11 @@ The projects listed in `testin.txt` are derived from plume-lib projects; each is These forks have had their (inferrable) annotations removed, and their typical checker build infrastructure disabled. The `./gradlew wpiManyTest` task defined in `checker/build.gradle` runs the `wpi-many.sh` script on these projects, and then checks that they typecheck afterwards. + The use of a hard fork means these tests may fail to compile under newer versions of the JDK. +When a new Java version is released, the projects may need to be updated and a new SHA commit +put in the `testin.txt` file. +TODO: List the required steps, such as using a newer version of Gradle. To add a new project (named `$PROJECT` below) to `testin.txt`, follow these steps: 1. Create a new GitHub repository under your own user name with the name "wpi-many-tests-$PROJECT". From 93f46756c2c4522c1f6453d11390fcba68555c87 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 11 Sep 2023 00:21:05 -0700 Subject: [PATCH 08/91] Use prerelease of Gradle 8.4 --- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 38440 zcmZ6SQ*<3%6s?oSw$a$O+t{{kpWwtNwr$(CZL_f&H8y*DImUnA_rrded+oL6w`Tc1 z#Mv!G{V@^ba0IDo(ixEwh?a02n}!DKIvnc^%1aoMq1+#kAhJLP_oX|jgEPsr;V`0k zL+b-2$5Bo(3c}Buz&-hcO_SO<6900dXYt$Ter6+Mad;kQkW8)Rc`!+|ia-jh^V%qoFb5JQ`

cw$=gQbRBDL9uPo|6z1d&0P$^g)iXXq+aG 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 CEjxVx delta 36364 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%0Dd@gG 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 Pg;r%T9-eUK&({9{Yi$yT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c30b486a892..10db5ee7e8c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.4-20230909005132+0000-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d421cc..0adc8e1a532 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in From c47f178b33dd33f63c1233d0c19eb491941ae735 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 11 Sep 2023 08:32:31 -0700 Subject: [PATCH 09/91] Improve diagnostics when clean command fails --- checker/bin/wpi-many.sh | 20 ++++++++++++-------- checker/bin/wpi.sh | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/checker/bin/wpi-many.sh b/checker/bin/wpi-many.sh index f8eaf21a50b..78e49ea7eae 100755 --- a/checker/bin/wpi-many.sh +++ b/checker/bin/wpi-many.sh @@ -287,14 +287,18 @@ do ls -al "$(pwd)" echo "Listing of ${REPO_FULLPATH}:" ls -al "${REPO_FULLPATH}" - echo "Listing of ${REPO_FULLPATH}/dljc-out:" - ls -al "${REPO_FULLPATH}"/dljc-out - for f in "${REPO_FULLPATH}"/dljc-out/* ; do - echo "==== start of tail of ${f} ====" - tail -n 2000 "${f}" - sleep 1 - echo "==== end of tail of ${f} ====" - done + if [ -d "${REPO_FULLPATH}/dljc-out" ] ; then + echo "Listing of ${REPO_FULLPATH}/dljc-out:" + ls -al "${REPO_FULLPATH}"/dljc-out + for f in "${REPO_FULLPATH}"/dljc-out/* ; do + echo "==== start of tail of ${f} ====" + tail -n 2000 "${f}" + sleep 1 + echo "==== end of tail of ${f} ====" + done + else + echo "Does not exist: ${REPO_FULLPATH}/dljc-out" + fi # If the result is unusable (i.e. wpi cannot run), # we don't need it for data analysis and we can diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 2aec1b8a76d..881bf3fd706 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -239,6 +239,7 @@ function configure_and_exec_dljc { # Remove old DLJC output. rm -rf dljc-out + mkdir -p "${DIR}/dljc-out/" # Ensure the project is clean before invoking DLJC. DLJC_CLEAN_STATUS=0 @@ -248,7 +249,8 @@ function configure_and_exec_dljc { echo "${WPI_RESULTS_AVAILABLE}" echo "Re-running clean command." # Cleaning failed. Re-run without piping output to /dev/null. - eval "${CLEAN_CMD}" < /dev/null || true + (eval "${CLEAN_CMD}" < /dev/null | tee -a "${DIR}/dljc-out/clean-output") || true + WPI_RESULTS_AVAILABLE="${WPI_RESULTS_AVAILABLE}\n$(cat "${DIR}/dljc-out/clean-output")" return fi From fc219645d8a85026496bd8a53132b2dfa6de7a84 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 11 Sep 2023 09:55:36 -0700 Subject: [PATCH 10/91] Better diagnostics --- checker/bin/wpi.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 881bf3fd706..6bb3baeec7f 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -249,8 +249,10 @@ function configure_and_exec_dljc { echo "${WPI_RESULTS_AVAILABLE}" echo "Re-running clean command." # Cleaning failed. Re-run without piping output to /dev/null. + echo "${CLEAN_CMD}" > "${DIR}/dljc-out/clean-output" (eval "${CLEAN_CMD}" < /dev/null | tee -a "${DIR}/dljc-out/clean-output") || true - WPI_RESULTS_AVAILABLE="${WPI_RESULTS_AVAILABLE}\n$(cat "${DIR}/dljc-out/clean-output")" + ls -al "${DIR}/dljc-out" + WPI_RESULTS_AVAILABLE="${WPI_RESULTS_AVAILABLE}"$'\n'"$(cat "${DIR}/dljc-out/clean-output")" return fi From fab08f20f6c8a5ff7be34627e07280905d833d2b Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 12 Sep 2023 08:25:45 -0700 Subject: [PATCH 11/91] In Java 21 case lables can be --- .../cfg/builder/CFGTranslationPhaseOne.java | 13 +++++++++--- .../framework/test/TestUtilities.java | 6 +++++- framework/tests/all-systems/Issue6173.java | 20 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 framework/tests/all-systems/Issue6173.java diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index b545f635c30..38afb60ce5a 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -46,6 +46,7 @@ import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.TypeParameterTree; @@ -2559,14 +2560,20 @@ private boolean casesAreExhaustive() { // It's an enumerated type. List enumConstants = ElementUtils.getEnumConstants(declaredTypeElement); - List caseLabels = new ArrayList<>(enumConstants.size()); + List<@Nullable Name> caseLabels = new ArrayList<>(enumConstants.size()); for (CaseTree caseTree : caseTrees) { for (ExpressionTree caseEnumConstant : TreeUtils.caseTreeGetExpressions(caseTree)) { - caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); + if (caseEnumConstant.getKind() == Kind.NULL_LITERAL) { + caseLabels.add(null); + } else { + caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); + } } } // Could also check that the values match. - boolean result = enumConstants.size() == caseLabels.size(); + boolean result = + (enumConstants.size() == caseLabels.size() + || enumConstants.size() == caseLabels.size() - 1); return result; } break; diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/TestUtilities.java b/framework-test/src/main/java/org/checkerframework/framework/test/TestUtilities.java index b161ed9a7da..d8faaa9f4c6 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/TestUtilities.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/TestUtilities.java @@ -58,6 +58,9 @@ public class TestUtilities { /** True if the JVM is version 18 or lower. */ public static final boolean IS_AT_MOST_18_JVM = SystemUtil.jreVersion <= 18; + /** True if the JVM is version 21 or above. */ + public static final boolean IS_AT_LEAST_21_JVM = SystemUtil.jreVersion >= 21; + static { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); OutputStream err = new ByteArrayOutputStream(); @@ -257,7 +260,8 @@ public static boolean isJavaTestFile(File file) { || (!IS_AT_LEAST_17_JVM && nextLine.contains("@below-java17-jdk-skip-test")) || (!IS_AT_MOST_17_JVM && nextLine.contains("@above-java17-jdk-skip-test")) || (!IS_AT_LEAST_18_JVM && nextLine.contains("@below-java18-jdk-skip-test")) - || (!IS_AT_MOST_18_JVM && nextLine.contains("@above-java18-jdk-skip-test"))) { + || (!IS_AT_MOST_18_JVM && nextLine.contains("@above-java18-jdk-skip-test")) + || (!IS_AT_LEAST_21_JVM && nextLine.contains("@below-java21-jdk-skip-test"))) { return false; } } diff --git a/framework/tests/all-systems/Issue6173.java b/framework/tests/all-systems/Issue6173.java new file mode 100644 index 00000000000..890293f615c --- /dev/null +++ b/framework/tests/all-systems/Issue6173.java @@ -0,0 +1,20 @@ +// @below-java21-jdk-skip-test +public class Issue6173 { + + static Object toGroupByQueryWithExtractor2(GroupBy groupBy) { + return switch (groupBy) { + case OWNER -> new Object(); + case CHANNEL -> new Object(); + case TOPIC -> new Object(); + case TEAM -> new Object(); + case null -> throw new IllegalArgumentException("GroupBy parameter is required"); + }; + } + + public enum GroupBy { + OWNER, + CHANNEL, + TOPIC, + TEAM; + } +} From 8b8c704e8cb830c22bc4ca3501bd81daac5f1609 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 12 Sep 2023 09:11:56 -0700 Subject: [PATCH 12/91] Fix. --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 38afb60ce5a..475d87ed2e3 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2560,20 +2560,16 @@ private boolean casesAreExhaustive() { // It's an enumerated type. List enumConstants = ElementUtils.getEnumConstants(declaredTypeElement); - List<@Nullable Name> caseLabels = new ArrayList<>(enumConstants.size()); + List caseLabels = new ArrayList<>(enumConstants.size()); for (CaseTree caseTree : caseTrees) { for (ExpressionTree caseEnumConstant : TreeUtils.caseTreeGetExpressions(caseTree)) { - if (caseEnumConstant.getKind() == Kind.NULL_LITERAL) { - caseLabels.add(null); - } else { + if (caseEnumConstant.getKind() != Kind.NULL_LITERAL) { caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); } } } // Could also check that the values match. - boolean result = - (enumConstants.size() == caseLabels.size() - || enumConstants.size() == caseLabels.size() - 1); + boolean result = enumConstants.size() == caseLabels.size(); return result; } break; From 8a6abdbfe89e1e2843a8fce057bf46c4ef875b72 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 12 Sep 2023 12:36:19 -0700 Subject: [PATCH 13/91] wpi-many tests under Java 21 --- checker/tests/wpi-many/testin.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/checker/tests/wpi-many/testin.txt b/checker/tests/wpi-many/testin.txt index b42871b7c94..0780aaac94b 100644 --- a/checker/tests/wpi-many/testin.txt +++ b/checker/tests/wpi-many/testin.txt @@ -1,7 +1,7 @@ -https://github.com/kelloggm/wpi-many-tests-bcel-util 9735be546d4286c033c45fa30186576f309723ab -https://github.com/kelloggm/wpi-many-tests-bibtex-clean 08919453b8868c1c50dde6cc9c82e82a6d1eba1b -https://github.com/kelloggm/wpi-many-tests-ensures-called-methods b5729881d621c12b9df6cbba818d8ad097ec32fb -https://github.com/kelloggm/wpi-many-tests-html-pretty-print 8ffe6fa5870f2f160bb713be28f823bd1c90fbda -https://github.com/kelloggm/-wpi-many-tests-bibtex-clean a9f2564f32bb874492aa1f9b0c5225f845a0e640 +https://github.com/kelloggm/wpi-many-tests-bcel-util bed1372c5c6de5c90f3a17d5ce228b882faa0da2 +https://github.com/kelloggm/wpi-many-tests-bibtex-clean 14770af9a6c973fd8a7920e22e40a501ba6fd2dc +https://github.com/kelloggm/wpi-many-tests-ensures-called-methods 20361e992d8c24aa48de62f1130f977d6b3c0ef1 +https://github.com/kelloggm/wpi-many-tests-html-pretty-print 96eb875ca7c9a67d6e3d61d634011c7354064dab +https://github.com/kelloggm/-wpi-many-tests-bibtex-clean c7715b8a60dd296926cc64d0bba9761caca88ab0 # This comment line tests that the commenting feature works (if it doesn't, then this line will be read and fail, as it's not a URL). -https://github.com/Nargeshdb/wpi-many-tests-owning-field 5f8fad65154dc14b3261b4c237ece70e265b3801 +https://github.com/Nargeshdb/wpi-many-tests-owning-field 1b73c81021f598717226243f99417c0cad54be82 From d4b74e94824902807c00d0d381d7a3c484b7e8e9 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 12 Sep 2023 13:52:10 -0700 Subject: [PATCH 14/91] Expand comment --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 31aa6c582a2..2083fd8cacc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,6 +14,7 @@ jobs: # * Everything depends on the canary jobs (the main jdk21 jobs), except those jobs themselves. # * Anything *_jdk11 or *_jdk17 or *_jdk20 depends on *_jdk21. ## TODO: The jdk20 jobs are a bit gratuitous. They are placeholders for jdk22 when it is released. +## Leaving those placeholders will ease the jdk22 edits to this file. # misc_jdk20 because JDK 20 adds more strict checking (e.g., Javadoc) - job: canary_jobs From 0a8b1b86093a118f47c4607d75e18f58ea9c206b Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 13 Sep 2023 06:07:09 -0700 Subject: [PATCH 15/91] Reinstate Java 20 support --- checker/bin/wpi-many.sh | 21 +++++++++++++++++++-- checker/bin/wpi.sh | 20 ++++++++++++++++++-- docs/manual/inference.tex | 3 ++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/checker/bin/wpi-many.sh b/checker/bin/wpi-many.sh index 748afe64ab6..f516431cd13 100755 --- a/checker/bin/wpi-many.sh +++ b/checker/bin/wpi-many.sh @@ -73,6 +73,13 @@ else has_java17="yes" fi +# shellcheck disable=SC2153 # testing for JAVA20_HOME, not a typo of JAVA_HOME +if [ "${JAVA20_HOME}" = "" ]; then + has_java20="no" +else + has_java20="yes" +fi + # shellcheck disable=SC2153 # testing for JAVA21_HOME, not a typo of JAVA_HOME if [ "${JAVA21_HOME}" = "" ]; then has_java21="no" @@ -99,6 +106,10 @@ if [ "${has_java_home}" = "yes" ]; then export JAVA17_HOME="${JAVA_HOME}" has_java17="yes" fi + if [ "${has_java20}" = "no" ] && [ "${java_version}" = 20 ]; then + export JAVA20_HOME="${JAVA_HOME}" + has_java20="yes" + fi if [ "${has_java21}" = "no" ] && [ "${java_version}" = 21 ]; then export JAVA21_HOME="${JAVA_HOME}" has_java21="yes" @@ -120,21 +131,27 @@ if [ "${has_java17}" = "yes" ] && [ ! -d "${JAVA17_HOME}" ]; then exit 1 fi +if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then + echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" + exit 1 +fi + if [ "${has_java21}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" exit 1 fi -if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java21}" = "no" ]; then +if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java20}" = "no" ] && [ "${has_java21}" = "no" ]; then if [ "${has_java_home}" = "yes" ]; then echo "Cannot determine Java version from JAVA_HOME" else - echo "No Java 8, 11, 17, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." + echo "No Java 8, 11, 17, 20, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." fi echo "JAVA_HOME = ${JAVA_HOME}" echo "JAVA8_HOME = ${JAVA8_HOME}" echo "JAVA11_HOME = ${JAVA11_HOME}" echo "JAVA17_HOME = ${JAVA17_HOME}" + echo "JAVA20_HOME = ${JAVA20_HOME}" echo "JAVA21_HOME = ${JAVA21_HOME}" command -v java java -version diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 6bb3baeec7f..2921b09b37d 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -68,6 +68,13 @@ else has_java17="yes" fi +# shellcheck disable=SC2153 # testing for JAVA20_HOME, not a typo of JAVA_HOME +if [ "${JAVA20_HOME}" = "" ]; then + has_java20="no" +else + has_java20="yes" +fi + # shellcheck disable=SC2153 # testing for JAVA21_HOME, not a typo of JAVA_HOME if [ "${JAVA21_HOME}" = "" ]; then has_java21="no" @@ -94,6 +101,10 @@ if [ "${has_java_home}" = "yes" ]; then export JAVA17_HOME="${JAVA_HOME}" has_java17="yes" fi + if [ "${has_java20}" = "no" ] && [ "${java_version}" = 20 ]; then + export JAVA20_HOME="${JAVA_HOME}" + has_java20="yes" + fi if [ "${has_java21}" = "no" ] && [ "${java_version}" = 21 ]; then export JAVA21_HOME="${JAVA_HOME}" has_java21="yes" @@ -115,16 +126,21 @@ if [ "${has_java17}" = "yes" ] && [ ! -d "${JAVA17_HOME}" ]; then exit 7 fi +if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA20_HOME}" ]; then + echo "JAVA20_HOME is set to a non-existent directory ${JAVA20_HOME}" + exit 7 +fi + if [ "${has_java21}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" exit 7 fi -if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java21}" = "no" ]; then +if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java20}" = "no" ] && [ "${has_java21}" = "no" ]; then if [ "${has_java_home}" = "yes" ]; then echo "Cannot determine Java version from JAVA_HOME" else - echo "No Java 8, 11, 17, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." + echo "No Java 8, 11, 17, 20, or 21 JDKs found. At least one of JAVA_HOME, JAVA8_HOME, JAVA11_HOME, JAVA17_HOME, or JAVA21_HOME must be set." fi echo "JAVA_HOME = ${JAVA_HOME}" echo "JAVA8_HOME = ${JAVA8_HOME}" diff --git a/docs/manual/inference.tex b/docs/manual/inference.tex index edede486caa..6dec18a09db 100644 --- a/docs/manual/inference.tex +++ b/docs/manual/inference.tex @@ -241,10 +241,11 @@ \verb|JAVA8_HOME|, \verb|JAVA11_HOME|, \verb|JAVA17_HOME|, or \verb|JAVA21_HOME| environment variables must be set. \item If set, the \verb|JAVA_HOME| environment variable must point to a - Java 8, 11, 17, or 21 JDK. + Java 8, 11, 17, 20, or 21 JDK. \item If set, the \verb|JAVA8_HOME| environment variable must point to a Java 8 JDK. \item If set, the \verb|JAVA11_HOME| environment variable must point to a Java 11 JDK. \item If set, the \verb|JAVA17_HOME| environment variable must point to a Java 17 JDK. +\item If set, the \verb|JAVA20_HOME| environment variable must point to a Java 20 JDK. \item If set, the \verb|JAVA21_HOME| environment variable must point to a Java 21 JDK. \item \ environment variable must point to a built copy of the Checker Framework. \item If set, the \verb|DLJC| environment variable must point to a copy of the \ script From eddfeed679d18f2909b4b216c42037aa2a20386b Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 13 Sep 2023 08:37:49 -0700 Subject: [PATCH 16/91] Fix cut-and-paste error --- checker/bin/wpi-many.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/checker/bin/wpi-many.sh b/checker/bin/wpi-many.sh index f516431cd13..91b8860c225 100755 --- a/checker/bin/wpi-many.sh +++ b/checker/bin/wpi-many.sh @@ -131,8 +131,8 @@ if [ "${has_java17}" = "yes" ] && [ ! -d "${JAVA17_HOME}" ]; then exit 1 fi -if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA21_HOME}" ]; then - echo "JAVA21_HOME is set to a non-existent directory ${JAVA21_HOME}" +if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA20_HOME}" ]; then + echo "JAVA20_HOME is set to a non-existent directory ${JAVA20_HOME}" exit 1 fi From 988847bbc66f065b1d7d2f6fa04c40304562903e Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 13 Sep 2023 09:41:38 -0700 Subject: [PATCH 17/91] Expand changelog --- docs/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 995ecd8d824..6ec8df3d1e7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,8 @@ Version 3.39.0 (October 1, 2023) **User-visible changes:** The Checker Framework runs under JDK 21 -- that is, it runs on a version 21 JVM. +It does not yet support all new Java 21 language features -- it may crash when +run on a program with new Java 21 language features. **Implementation details:** From c0f68234ee22b3d32c980754889f88eb378d7069 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 13 Sep 2023 10:59:17 -0700 Subject: [PATCH 18/91] Fix lint. --- build.gradle | 18 ++++++++++++++---- .../all-systems/{ => java21}/Issue6173.java | 0 2 files changed, 14 insertions(+), 4 deletions(-) rename framework/tests/all-systems/{ => java21}/Issue6173.java (100%) diff --git a/build.gradle b/build.gradle index 195d2a0a6fc..f4fb356b81e 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ ext { // This call to `isCompatibleWith` causes a Gradle run-time failure: "No signature of method". // isJava17compatible = JavaVersion.isCompatibleWith(JavaVersion.VERSION_17) isJava17orHigher = JavaVersion.current() >= JavaVersion.VERSION_17 + isJava21orHigher = JavaVersion.current() >= JavaVersion.VERSION_21 // As of 2023-09-08, delombok doesn't yet support JDK 21; see https://projectlombok.org/changelog . skipDelombok = JavaVersion.current() == JavaVersion.VERSION_21 @@ -201,6 +202,10 @@ allprojects { doNotFormat += ['**/java17/', '**/*record*/'] } + if (!isJava21orHigher) { + doNotFormat += ['**/java21/'] + } + format 'misc', { // define the files to apply `misc` to target '*.md', '*.tex', '.gitignore', 'Makefile' @@ -355,13 +360,18 @@ allprojects { options.failOnError = true options.deprecation = true + // -options: To not get a warning about missing bootstrap classpath (when using Java 9 and `-source 8`). + // -fallthrough: Don't check fallthroughs. Instead, use Error Prone. Its + // warnings are suppressible with a "// fall through" comment. + String lint = '-Xlint:-options,-fallthrough' + if(isJava21orHigher) { + // TODO: Ignore these for now, we may want to review and suppress each one later. + lint +=',-this-escape' + } options.compilerArgs += [ '-g', '-Werror', - // -options: To not get a warning about missing bootstrap classpath (when using Java 9 and `-source 8`). - // -fallthrough: Don't check fallthroughs. Instead, use Error Prone. Its - // warnings are suppressible with a "// fall through" comment. - '-Xlint:-options,-fallthrough', + lint, '-Xlint', ] diff --git a/framework/tests/all-systems/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java similarity index 100% rename from framework/tests/all-systems/Issue6173.java rename to framework/tests/all-systems/java21/Issue6173.java From bcaa0a5f039faef3383821b0ae9807c6018c4a59 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 13 Sep 2023 11:00:48 -0700 Subject: [PATCH 19/91] Revert previous attempt to support case null --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 475d87ed2e3..b545f635c30 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -46,7 +46,6 @@ import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.TypeParameterTree; @@ -2563,9 +2562,7 @@ private boolean casesAreExhaustive() { List caseLabels = new ArrayList<>(enumConstants.size()); for (CaseTree caseTree : caseTrees) { for (ExpressionTree caseEnumConstant : TreeUtils.caseTreeGetExpressions(caseTree)) { - if (caseEnumConstant.getKind() != Kind.NULL_LITERAL) { - caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); - } + caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); } } // Could also check that the values match. From bbe621cfdb7e158e21f72edfa44c01ad9f3928c7 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 13 Sep 2023 11:35:54 -0700 Subject: [PATCH 20/91] Correction. --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index b545f635c30..024dee51759 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -46,6 +46,7 @@ import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.TypeParameterTree; @@ -2341,7 +2342,7 @@ private SwitchBuilder(Tree switchTree) { // This can be extended to handle case statements as well as case rules. boolean noFallthroughToHere = TreeUtils.isCaseRule(caseTree); boolean isLastOfExhaustive = - isLastExceptDefault && casesAreExhaustive() && noFallthroughToHere; + isLastExceptDefault && allCasesAreEnumerated() && noFallthroughToHere; buildCase(caseTree, i, isLastOfExhaustive); } } @@ -2545,7 +2546,7 @@ private void buildCase(CaseTree tree, int index, boolean isLastOfExhaustive) { * * @return true if the cases are exhaustive */ - private boolean casesAreExhaustive() { + private boolean allCasesAreEnumerated() { TypeMirror selectorTypeMirror = TreeUtils.typeOf(selectorExprTree); switch (selectorTypeMirror.getKind()) { @@ -2562,6 +2563,10 @@ private boolean casesAreExhaustive() { List caseLabels = new ArrayList<>(enumConstants.size()); for (CaseTree caseTree : caseTrees) { for (ExpressionTree caseEnumConstant : TreeUtils.caseTreeGetExpressions(caseTree)) { + if (caseEnumConstant.getKind() != Kind.IDENTIFIER) { + // This is not a simple switch on an enum, so all cases can't be enumerated. + return false; + } caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); } } From 1e10665dfe3d949f57edbc960f90b1f1e3b6762f Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 14 Sep 2023 08:13:40 -0700 Subject: [PATCH 21/91] Checkpoint. --- .../cfg/builder/CFGTranslationPhaseOne.java | 18 ++++---- .../checkerframework/javacutil/TreeUtils.java | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 024dee51759..fe7a466748a 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2440,13 +2440,13 @@ private void buildSwitchExpressionVar() { /** * Build the CFG for the given case tree. * - * @param tree a case tree whose CFG to build - * @param index the index of the case tree in {@link #caseBodyLabels} + * @param caseTree a case caseTree whose CFG to build + * @param index the index of the case caseTree in {@link #caseBodyLabels} * @param isLastOfExhaustive true if this is the last case of an exhaustive switch statement, * with no fallthrough to it. In other words, no test of the labels is necessary. */ - private void buildCase(CaseTree tree, int index, boolean isLastOfExhaustive) { - boolean isDefaultCase = TreeUtils.isDefaultCaseTree(tree); + private void buildCase(CaseTree caseTree, int index, boolean isLastOfExhaustive) { + boolean isDefaultCase = TreeUtils.isDefaultCaseTree(caseTree); // If true, no test of labels is necessary. // Unfortunately, if isLastOfExhaustive==TRUE, no flow-sensitive refinement occurs // within the body of the CaseNode. In the future, that can be performed, but it @@ -2462,21 +2462,21 @@ private void buildCase(CaseTree tree, int index, boolean isLastOfExhaustive) { if (!isTerminalCase) { // A case expression exists, and it needs to be tested. ArrayList exprs = new ArrayList<>(); - for (ExpressionTree exprTree : TreeUtils.caseTreeGetExpressions(tree)) { + for (ExpressionTree exprTree : TreeUtils.caseTreeGetExpressions(caseTree)) { exprs.add(scan(exprTree, null)); } - CaseNode test = new CaseNode(tree, selectorExprAssignment, exprs, env.getTypeUtils()); + CaseNode test = new CaseNode(caseTree, selectorExprAssignment, exprs, env.getTypeUtils()); extendWithNode(test); extendWithExtendedNode(new ConditionalJump(thisBodyLabel, nextCaseLabel)); } // Handle the case body addLabelForNextNode(thisBodyLabel); - if (tree.getStatements() != null) { + if (caseTree.getStatements() != null) { // This is a switch labeled statement group. // A "switch labeled statement group" is a "case L:" label along with its code. // The code either ends with a "yield" statement, or it falls through. - for (StatementTree stmt : tree.getStatements()) { + for (StatementTree stmt : caseTree.getStatements()) { scan(stmt, null); } // Handle possible fallthrough by adding jump to next body. @@ -2487,7 +2487,7 @@ private void buildCase(CaseTree tree, int index, boolean isLastOfExhaustive) { // This is either the default case or a switch labeled rule (which appears in a // switch expression). // A "switch labeled rule" is a "case L ->" label along with its code. - Tree bodyTree = TreeUtils.caseTreeGetBody(tree); + Tree bodyTree = TreeUtils.caseTreeGetBody(caseTree); if (!TreeUtils.isSwitchStatement(switchTree) && bodyTree instanceof ExpressionTree) { buildSwitchExpressionResult((ExpressionTree) bodyTree); } else { diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index b9ab0acd965..9e299d226d0 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -125,6 +125,9 @@ private TreeUtils() { /** The CaseTree.getExpressions method for Java 12 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETEXPRESSIONS; + /** The CaseTree.getLabels method for Java 21 and higher; null otherwise. */ + private static final @Nullable Method CASETREE_GETLABELS; + /** The CaseTree.getBody method for Java 12 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETBODY; @@ -260,8 +263,10 @@ private TreeUtils() { } if (atLeastJava21) { TREEMAKER_SELECT = TreeMaker.class.getMethod("Select", JCExpression.class, Symbol.class); + CASETREE_GETLABELS = CaseTree.class.getDeclaredMethod("getLabels"); } else { TREEMAKER_SELECT = null; + CASETREE_GETLABELS = null; } } catch (ClassNotFoundException | NoSuchMethodException e) { Error err = new AssertionError("Unexpected error in TreeUtils static initializer"); @@ -2401,6 +2406,47 @@ public static List caseTreeGetExpressions(CaseTree cas } } + /** + * Get the list of expressions from a case expression. For the default case, this is empty. + * Otherwise, in JDK 11 and earlier, this is a singleton list. In JDK 12 onwards, there can be + * multiple expressions per case. + * + * @param caseTree the case expression to get the expressions from + * @return the list of {@code CaseLabelTree}s in the case + */ + public static List caseTreeGetLabels(CaseTree caseTree) { + try { + if (atLeastJava21) { + @SuppressWarnings({"unchecked", "nullness"}) + // These are caseLabelTrees. + @NonNull List caseLabelTrees = + (List) CASETREE_GETLABELS.invoke(caseTree); + List unwrappedLabel = new ArrayList<>(); + for (Tree caseLabelTree : caseLabelTrees) { + // if(caseLabelTrees) + + } + return unwrappedLabel; + } else if (atLeastJava12) { + @SuppressWarnings({"unchecked", "nullness"}) + @NonNull List result = + (List) CASETREE_GETEXPRESSIONS.invoke(caseTree); + return result; + } else { + @SuppressWarnings("nullness") + ExpressionTree expression = (ExpressionTree) CASETREE_GETEXPRESSION.invoke(caseTree); + if (expression == null) { + return Collections.emptyList(); + } else { + return Collections.singletonList(expression); + } + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.caseTreeGetExpressions: reflection failed for tree: %s", caseTree, e); + } + } + /** * Returns the body of the case statement if it is of the form {@code case -> * }. This method should only be called if {@link CaseTree#getStatements()} returns From eacabbcbd20e5de7b214f7b3ae7047e04a246997 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Thu, 14 Sep 2023 09:20:52 -0700 Subject: [PATCH 22/91] Use prerelease Ubuntu, install JDK 17 in each Docker image --- checker/bin-devel/Dockerfile-README | 10 ++ checker/bin-devel/Dockerfile-ubuntu-jdk11 | 3 +- .../bin-devel/Dockerfile-ubuntu-jdk11-plus | 4 +- checker/bin-devel/Dockerfile-ubuntu-jdk20 | 68 ++++++++++++ .../bin-devel/Dockerfile-ubuntu-jdk20-plus | 102 ++++++++++++++++++ checker/bin-devel/Dockerfile-ubuntu-jdk21 | 3 +- .../bin-devel/Dockerfile-ubuntu-jdk21-plus | 3 +- 7 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 checker/bin-devel/Dockerfile-ubuntu-jdk20 create mode 100644 checker/bin-devel/Dockerfile-ubuntu-jdk20-plus diff --git a/checker/bin-devel/Dockerfile-README b/checker/bin-devel/Dockerfile-README index 7b4a5c7c5e5..a3e75be6bb6 100644 --- a/checker/bin-devel/Dockerfile-README +++ b/checker/bin-devel/Dockerfile-README @@ -51,6 +51,16 @@ export JDKVER=jdk17-plus export PROJECT=cf create_upload_docker_image +export OS=ubuntu +export JDKVER=jdk20 +export PROJECT=cf +create_upload_docker_image + +export OS=ubuntu +export JDKVER=jdk20-plus +export PROJECT=cf +create_upload_docker_image + export OS=ubuntu export JDKVER=jdk21 export PROJECT=cf diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11 b/checker/bin-devel/Dockerfile-ubuntu-jdk11 index 291908d9281..27eda4a3df8 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11 @@ -19,7 +19,8 @@ RUN export DEBIAN_FRONTEND=noninteractive \ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ - openjdk-11-jdk + openjdk-11-jdk \ + openjdk-17-jdk # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus index dabbdb2182d..8371bfc819f 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus @@ -19,7 +19,9 @@ RUN export DEBIAN_FRONTEND=noninteractive \ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ - openjdk-11-jdk + openjdk-11-jdk \ + openjdk-17-jdk \ +&& sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20 b/checker/bin-devel/Dockerfile-ubuntu-jdk20 new file mode 100644 index 00000000000..64678b52c55 --- /dev/null +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk20 @@ -0,0 +1,68 @@ +# Create a Docker image that is ready to run the main Checker Framework tests, +# using JDK 20. +# (This is OpenJDK, not Oracle JDK. There are different instructions for +# installing a LTS release of Java.) +# To convert this file to use a newer JDK, search (from the top level of the +# Checker Framework and Annotation Tools repositories) for: (java|jdk).?20\b + +# "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. +FROM ubuntu:rolling +MAINTAINER Michael Ernst + +# According to +# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/: +# * Put "apt-get update" and "apt-get install" and "apt cleanup" in the same RUN command. +# * Do not run "apt-get upgrade"; instead get upstream to update. + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& apt-get -y install aptitude \ +&& aptitude -y install \ + apt-utils + +# ca-certificates-java is a dependency of openjdk-20-jdk, but the installation +# process seems to fail sometimes when only openjdk-20-jdk is specified. +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + ca-certificates-java \ +&& aptitude -y install \ + openjdk-17-jdk \ + openjdk-20-jdk + +# Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + ant \ + cpp \ + git \ + jq \ + libcurl3-gnutls \ + make \ + maven \ + python3-distutils \ + python3-requests \ + unzip \ + wget \ +&& aptitude -y install \ + jtreg6 + +# Maven 3.8.7 is the default on Ubuntu 23.04. +# (Don't try to use a variable here for the Maven version.) +# RUN export DEBIAN_FRONTEND=noninteractive \ +# && wget https://mirrors.sonic.net/apache/maven/maven-3/3.9.2/binaries/apache-maven-3.9.2-bin.tar.gz \ +# && tar xzvf apache-maven-3.9.2-bin.tar.gz +# ENV PATH="/apache-maven-3.9.2/bin:$PATH" + +# Bug fix to make jtreg runnable: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=754942;msg=2 +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + default-jre-headless + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get autoremove \ +&& apt-get clean \ +&& rm -rf /var/lib/apt/lists/* diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus new file mode 100644 index 00000000000..a9cd51dd9a2 --- /dev/null +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus @@ -0,0 +1,102 @@ +# Create a Docker image that is ready to run the full Checker Framework tests, +# including building the manual and Javadoc, using JDK 20. +# (This is OpenJDK, not Oracle JDK. There are different instructions for +# installing a LTS release of Java.) +# To convert this file to use a newer JDK, search (from the top level of the +# Checker Framework and Annotation Tools repositories) for: (java|jdk).?20\b + +# "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. +FROM ubuntu:rolling +MAINTAINER Michael Ernst + +# According to +# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/: +# * Put "apt-get update" and "apt-get install" and "apt cleanup" in the same RUN command. +# * Do not run "apt-get upgrade"; instead get upstream to update. + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& apt-get -y install aptitude \ +&& aptitude -y install \ + apt-utils + +# ca-certificates-java is a dependency of openjdk-20-jdk, but the installation +# process seems to fail sometimes when only openjdk-20-jdk is specified. +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + ca-certificates-java \ +&& aptitude -y install \ + openjdk-17-jdk \ + openjdk-20-jdk + +# Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + ant \ + cpp \ + git \ + jq \ + libcurl3-gnutls \ + make \ + maven \ + python3-distutils \ + python3-requests \ + unzip \ + wget \ +&& aptitude -y install \ + jtreg6 + +# Maven 3.8.7 is the default on Ubuntu 23.04, so the below is not needed. +# (Don't try to use a variable here for the Maven version.) +# RUN export DEBIAN_FRONTEND=noninteractive \ +# && wget https://mirrors.sonic.net/apache/maven/maven-3/3.9.2/binaries/apache-maven-3.9.2-bin.tar.gz \ +# && tar xzvf apache-maven-3.9.2-bin.tar.gz +# ENV PATH="/apache-maven-3.9.2/bin:$PATH" + +# Bug fix to make jtreg runnable: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=754942;msg=2 +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + default-jre-headless + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + autoconf \ + devscripts \ + dia \ + hevea \ + imagemagick \ + junit \ + latexmk \ + librsvg2-bin \ + libasound2-dev libcups2-dev libfontconfig1-dev \ + libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev \ + pdf2svg \ + rsync \ + shellcheck \ + texlive-font-utils \ + texlive-fonts-recommended \ + texlive-latex-base \ + texlive-latex-extra \ + texlive-latex-recommended + +# `pipx ensurepath` only adds to the path in newly-started shells. +# BUT, setting the path for the current user is not enough. +# Azure creates a new user and runs jobs as it. +# So, install into /usr/local/bin which is already on every user's path. +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get -qqy update \ +&& aptitude -y install \ + pipx \ +&& PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install black \ +&& PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install flake8 \ +&& PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install html5validator + +RUN export DEBIAN_FRONTEND=noninteractive \ +&& apt-get autoremove \ +&& apt-get clean \ +&& rm -rf /var/lib/apt/lists/* diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk21 b/checker/bin-devel/Dockerfile-ubuntu-jdk21 index 093d13a0a8d..1b1915312a0 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk21 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21 @@ -6,7 +6,7 @@ # Checker Framework and Annotation Tools repositories) for: (java|jdk).?21\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. -FROM ubuntu:rolling +FROM ubuntu:23.10 MAINTAINER Michael Ernst # According to @@ -23,6 +23,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ + openjdk-17-jdk \ openjdk-21-jdk # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus index 596a5bdf3b3..d64487d6230 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus @@ -6,7 +6,7 @@ # Checker Framework and Annotation Tools repositories) for: (java|jdk).?21\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. -FROM ubuntu:rolling +FROM ubuntu:23.10 MAINTAINER Michael Ernst ## Keep this file in sync with ../../docs/manual/troubleshooting.tex @@ -25,6 +25,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ + openjdk-17-jdk \ openjdk-21-jdk # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . From 19b03748b7129f676ed1d93d7c7743b930ad3a13 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 14 Sep 2023 11:20:19 -0700 Subject: [PATCH 23/91] All the JEP 441 tests do not crash. --- .../cfg/builder/CFGTranslationPhaseOne.java | 2 +- .../CheckerFrameworkPerDirectoryTest.java | 3 +- .../tests/all-systems/java21/JEP441.java | 212 ++++++++++++++++++ .../checkerframework/javacutil/TreeUtils.java | 120 +++++++++- 4 files changed, 326 insertions(+), 11 deletions(-) create mode 100644 framework/tests/all-systems/java21/JEP441.java diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index fe7a466748a..235a52d36cb 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2462,7 +2462,7 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastOfExhaustive) if (!isTerminalCase) { // A case expression exists, and it needs to be tested. ArrayList exprs = new ArrayList<>(); - for (ExpressionTree exprTree : TreeUtils.caseTreeGetExpressions(caseTree)) { + for (Tree exprTree : TreeUtils.caseTreeGetLabels(caseTree)) { exprs.add(scan(exprTree, null)); } CaseNode test = new CaseNode(caseTree, selectorExprAssignment, exprs, env.getTypeUtils()); diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java index 3bbd347943e..6dfa2ed05fb 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java @@ -138,7 +138,8 @@ protected CheckerFrameworkPerDirectoryTest( this.testDir = testDir; this.classpathExtra = classpathExtra; this.checkerOptions = new ArrayList<>(Arrays.asList(checkerOptions)); - this.checkerOptions.add("-AajavaChecks"); + // TODO: Reinstate, javaparser crashes on new Java 21 language features. + // this.checkerOptions.add("-AajavaChecks"); } @Test diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java new file mode 100644 index 00000000000..ce6fbfae40d --- /dev/null +++ b/framework/tests/all-systems/java21/JEP441.java @@ -0,0 +1,212 @@ +// @below-java21-jdk-skip-test +// These are examples copied from: +// https://openjdk.org/jeps/441 +public class JEP441 { + + // We enhance switch statements and expressions in four ways: + // * Improve enum constant case labels + // * Extend case labels to include patterns and null in addition to constants + // * Broaden the range of types permitted for the selector expressions of both switch statements + // and switch expressions (along with the required richer analysis of exhaustiveness of switch + // blocks) + // * Allow optional when clauses to follow case labels. + + // Prior to Java 21 + static String formatter(Object obj) { + String formatted = "unknown"; + if (obj instanceof Integer i) { + formatted = String.format("int %d", i); + } else if (obj instanceof Long l) { + formatted = String.format("long %d", l); + } else if (obj instanceof Double d) { + formatted = String.format("double %f", d); + } else if (obj instanceof String s) { + formatted = String.format("String %s", s); + } + return formatted; + } + static void formatterPatternSwitchStatement(Object obj) { + switch (obj) { + case Integer i: String.format("int %d", i); break; + case Long l :String.format("long %d", l);break; + case Double d :String.format("double %f", d);break; + case String s :String.format("String %s", s);break; + default : obj.toString(); + }; + } + + static String formatterPatternSwitch(Object obj) { + return switch (obj) { + case Integer i -> String.format("int %d", i); + case Long l -> String.format("long %d", l); + case Double d -> String.format("double %f", d); + case String s -> String.format("String %s", s); + default -> obj.toString(); + }; + } + // As of Java 21 + static void testFooBarNew(String s) { + switch (s) { + case null -> System.out.println("Oops"); + case "Foo", "Bar" -> System.out.println("Great"); + default -> System.out.println("Ok"); + } + } + + static void testStringOld(String response) { + switch (response) { + case null -> { } + case String s -> { + if (s.equalsIgnoreCase("YES")) + System.out.println("You got it"); + else if (s.equalsIgnoreCase("NO")) + System.out.println("Shame"); + else + System.out.println("Sorry?"); + } + } + } + + static void testStringNew(String response) { + switch (response) { + case null -> { } + case String s + when s.equalsIgnoreCase("YES") -> { + System.out.println("You got it"); + } + case String s + when s.equalsIgnoreCase("NO") -> { + System.out.println("Shame"); + } + case String s -> { + System.out.println("Sorry?"); + } + } + } + + static void testStringEnhanced(String response) { + switch (response) { + case null -> { } + case "y", "Y" -> { + System.out.println("You got it"); + } + case "n", "N" -> { + System.out.println("Shame"); + } + case String s + when s.equalsIgnoreCase("YES") -> { + System.out.println("You got it"); + } + case String s + when s.equalsIgnoreCase("NO") -> { + System.out.println("Shame"); + } + case String s -> { + System.out.println("Sorry?"); + } + } + } + + sealed interface CardClassification permits Suit, Tarot {} + public enum Suit implements CardClassification { CLUBS, DIAMONDS, HEARTS, SPADES } + final class Tarot implements CardClassification {} + + static void exhaustiveSwitchWithoutEnumSupport(CardClassification c) { + switch (c) { + case Suit s when s == Suit.CLUBS -> { + System.out.println("It's clubs"); + } + case Suit s when s == Suit.DIAMONDS -> { + System.out.println("It's diamonds"); + } + case Suit s when s == Suit.HEARTS -> { + System.out.println("It's hearts"); + } + case Suit s -> { + System.out.println("It's spades"); + } + case Tarot t -> { + System.out.println("It's a tarot"); + } + } + } + + static void exhaustiveSwitchWithBetterEnumSupport(CardClassification c) { + switch (c) { + case Suit.CLUBS -> { + System.out.println("It's clubs"); + } + case Suit.DIAMONDS -> { + System.out.println("It's diamonds"); + } + case Suit.HEARTS -> { + System.out.println("It's hearts"); + } + case Suit.SPADES -> { + System.out.println("It's spades"); + } + case Tarot t -> { + System.out.println("It's a tarot"); + } + } + } + sealed interface Currency permits Coin {} + enum Coin implements Currency { HEADS, TAILS } + + static void goodEnumSwitch1(Currency c) { + switch (c) { + case Coin.HEADS -> { // Qualified name of enum constant as a label + System.out.println("Heads"); + } + case Coin.TAILS -> { + System.out.println("Tails"); + } + } + } + + static void goodEnumSwitch2(Coin c) { + switch (c) { + case HEADS -> { + System.out.println("Heads"); + } + case Coin.TAILS -> { // Unnecessary qualification but allowed + System.out.println("Tails"); + } + } + } + + record Point(int i, int j) {} + enum Color { RED, GREEN, BLUE; } + + static void typeTester(Object obj) { + switch (obj) { + case null -> System.out.println("null"); + case String s -> System.out.println("String"); + case Color c -> System.out.println("Color: " + c.toString()); + case Point p -> System.out.println("Record class: " + p.toString()); + case int[] ia -> System.out.println("Array of ints of length" + ia.length); + default -> System.out.println("Something else"); + } + } + + static void first(Object obj) { + switch (obj) { + case String s -> + System.out.println("A string: " + s); + case CharSequence cs -> + System.out.println("A sequence of length " + cs.length()); + default -> { + break; + } + } + } + + void fragment( Integer i ){ + // TODO: This would be a good test case for the Value Checker. +// switch (i) { +// case -1, 1 -> ... // Special cases +// case Integer j when j > 0 -> ... // Positive integer cases +// case Integer j -> ... // All the remaining integers +// } + } +} diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 9e299d226d0..128fefcbb9f 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -125,9 +125,6 @@ private TreeUtils() { /** The CaseTree.getExpressions method for Java 12 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETEXPRESSIONS; - /** The CaseTree.getLabels method for Java 21 and higher; null otherwise. */ - private static final @Nullable Method CASETREE_GETLABELS; - /** The CaseTree.getBody method for Java 12 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETBODY; @@ -152,6 +149,17 @@ private TreeUtils() { /** The BindingPatternTree.getVariable method for Java 16 and higher; null otherwise. */ private static final @Nullable Method BINDINGPATTERNTREE_GETVARIABLE; + /** The CaseTree.getLabels method for Java 21 and higher; null otherwise. */ + private static final @Nullable Method CASETREE_GETLABELS; + + /** + * The ConstantCaseLabelTree.getConstantExpression method for Java 21 and higher; null otherwise. + */ + private static final @Nullable Method CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION; + + /** The PatternCaseLabelTree.getPattern method for Java 21 and higher; null otherwise. */ + private static final @Nullable Method PATTERNCASELABELTREE_GETPATTERN; + /** * The {@code TreeMaker.Select(JCExpression, Symbol)} method. Return type changes for JDK21+. Only * needs to be used while the code is compiled with JDK below 21. @@ -261,12 +269,23 @@ private TreeUtils() { INSTANCEOFTREE_GETPATTERN = null; BINDINGPATTERNTREE_GETVARIABLE = null; } + if (atLeastJava21) { + Class constantCaseLabelTreeClass = + Class.forName("com.sun.source.tree.ConstantCaseLabelTree"); + CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION = + constantCaseLabelTreeClass.getMethod("getConstantExpression"); + Class patternCaseLabelTreeClass = + Class.forName("com.sun.source.tree.PatternCaseLabelTree"); + PATTERNCASELABELTREE_GETPATTERN = patternCaseLabelTreeClass.getMethod("getPattern"); + TREEMAKER_SELECT = TreeMaker.class.getMethod("Select", JCExpression.class, Symbol.class); CASETREE_GETLABELS = CaseTree.class.getDeclaredMethod("getLabels"); } else { TREEMAKER_SELECT = null; CASETREE_GETLABELS = null; + CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION = null; + PATTERNCASELABELTREE_GETPATTERN = null; } } catch (ClassNotFoundException | NoSuchMethodException e) { Error err = new AssertionError("Unexpected error in TreeUtils static initializer"); @@ -2350,7 +2369,7 @@ public static boolean sameTree(ExpressionTree expr1, ExpressionTree expr2) { * @return true if {@code caseTree} is the default case for a switch statement or expression */ public static boolean isDefaultCaseTree(CaseTree caseTree) { - return caseTreeGetExpressions(caseTree).isEmpty(); + return caseTreeGetLabels(caseTree).isEmpty(); } /** @@ -2421,12 +2440,17 @@ public static List caseTreeGetLabels(CaseTree caseTree) { // These are caseLabelTrees. @NonNull List caseLabelTrees = (List) CASETREE_GETLABELS.invoke(caseTree); - List unwrappedLabel = new ArrayList<>(); - for (Tree caseLabelTree : caseLabelTrees) { - // if(caseLabelTrees) - + List unWrappedLabels = new ArrayList<>(); + for (Tree caseLabel : caseLabelTrees) { + if (isDefaultCaseLabelTree(caseLabel)) { + return Collections.emptyList(); + } else if (isConstantCaseLabelTree(caseLabel)) { + unWrappedLabels.add(constantCaseLabelTreeGetConstantExpression(caseLabel)); + } else if (isPatternCaseLabelTree(caseLabel)) { + unWrappedLabels.add(patternCaseLabelTreeGetPattern(caseLabel)); + } } - return unwrappedLabel; + return unWrappedLabels; } else if (atLeastJava12) { @SuppressWarnings({"unchecked", "nullness"}) @NonNull List result = @@ -2447,6 +2471,84 @@ public static List caseTreeGetLabels(CaseTree caseTree) { } } + /** + * Returns whether {@code tree} is a {@code DefaultCaseLabelTree}. + * + * @param tree a tree to check + * @return whether {@code tree} is a {@code DefaultCaseLabelTree} + */ + public static boolean isDefaultCaseLabelTree(Tree tree) { + return tree.getKind().name().contentEquals("DEFAULT_CASE_LABEL"); + } + + /** + * Returns whether {@code tree} is a {@code ConstantCaseLabelTree}. + * + * @param tree a tree to check + * @return whether {@code tree} is a {@code ConstantCaseLabelTree} + */ + public static boolean isConstantCaseLabelTree(Tree tree) { + return tree.getKind().name().contentEquals("CONSTANT_CASE_LABEL"); + } + + /** + * Wrapper around {@code ConstantCaseLabelTree#getConstantExpression}. + * + * @param constantCaseLabelTree a ConstantCaseLabelTree tree + * @return the expression in the {@code constantCaseLabelTree} + */ + public static ExpressionTree constantCaseLabelTreeGetConstantExpression( + Tree constantCaseLabelTree) { + if (atLeastJava21) { + try { + @SuppressWarnings("nullness") + ExpressionTree ret = + (ExpressionTree) + CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION.invoke(constantCaseLabelTree); + return ret; + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.constantCaseLabelTreeGetConstantExpression: reflection failed for tree: %s", + constantCaseLabelTree, e); + } + } else { + throw new BugInCF( + "TreeUtils.constantCaseLabelTreeGetConstantExpression: requires at least Java 21"); + } + } + + /** + * Returns whether {@code tree} is a {@code PatternCaseLabelTree}. + * + * @param tree a tree to check + * @return whether {@code tree} is a {@code PatternCaseLabelTree} + */ + public static boolean isPatternCaseLabelTree(Tree tree) { + return tree.getKind().name().contentEquals("PATTERN_CASE_LABEL"); + } + + /** + * Wrapper around {@code PatternCaseLabelTree#getPattern}. + * + * @param patternCaseLabelTree a PatternCaseLabelTree tree + * @return the {@code PatternTree} in the {@code patternCaseLabelTree} + */ + public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { + if (atLeastJava21) { + try { + @SuppressWarnings("nullness") + Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); + return ret; + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.patternCaseLabelTreeGetPattern: reflection failed for tree: %s", + patternCaseLabelTree, e); + } + } else { + throw new BugInCF("TreeUtils.patternCaseLabelTreeGetPattern: requires at least Java 21"); + } + } + /** * Returns the body of the case statement if it is of the form {@code case -> * }. This method should only be called if {@link CaseTree#getStatements()} returns From 5e9aa30b8f966d181893c8bf2aff75d13b2426c8 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Thu, 14 Sep 2023 15:38:53 -0700 Subject: [PATCH 24/91] Make wpi.sh message less scary --- checker/bin/wpi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 616c6e025af..227077bfefb 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -296,7 +296,7 @@ function configure_and_exec_dljc { # exists, which means that WPI produced no output. When that happens, the reason is usually that the Checker # Framework crashed, so output the log file for easier debugging. wpi_no_output_message="No WPI outputs were discovered; it is likely that WPI failed or the Checker Framework crashed" - echo "About to test: \$(cat \"${dljc_stdout}\") == \"${wpi_no_output_message}\"" + echo "About to look for WPI failure in ${dljc_stdout} ." if [[ $(cat "${dljc_stdout}") == *"${wpi_no_output_message}"* ]]; then wpi_log_path="${DIR}"/dljc-out/wpi-stdout.log echo "=== ${wpi_no_output_message}: start of ${wpi_log_path} ===" From fd25570d041e962f8bfbdb52b5410970522cb60c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Thu, 14 Sep 2023 15:47:04 -0700 Subject: [PATCH 25/91] Remove scary progress message --- checker/bin/wpi.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/checker/bin/wpi.sh b/checker/bin/wpi.sh index 227077bfefb..9dc940db9aa 100755 --- a/checker/bin/wpi.sh +++ b/checker/bin/wpi.sh @@ -296,7 +296,6 @@ function configure_and_exec_dljc { # exists, which means that WPI produced no output. When that happens, the reason is usually that the Checker # Framework crashed, so output the log file for easier debugging. wpi_no_output_message="No WPI outputs were discovered; it is likely that WPI failed or the Checker Framework crashed" - echo "About to look for WPI failure in ${dljc_stdout} ." if [[ $(cat "${dljc_stdout}") == *"${wpi_no_output_message}"* ]]; then wpi_log_path="${DIR}"/dljc-out/wpi-stdout.log echo "=== ${wpi_no_output_message}: start of ${wpi_log_path} ===" From e937dc928f35282cb8a5031719d23f471dbf13d6 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 16 Sep 2023 09:18:27 -0700 Subject: [PATCH 26/91] Add `update-java-alternatives` command --- checker/bin-devel/Dockerfile-ubuntu-jdk11 | 3 ++- checker/bin-devel/Dockerfile-ubuntu-jdk11-plus | 2 +- checker/bin-devel/Dockerfile-ubuntu-jdk20 | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11 b/checker/bin-devel/Dockerfile-ubuntu-jdk11 index 27eda4a3df8..0bf4adca84e 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11 @@ -20,7 +20,8 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ && aptitude -y install \ openjdk-11-jdk \ - openjdk-17-jdk + openjdk-17-jdk \ +&& update-java-alternatives -s java-1.11.0-openjdk-amd64 # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus index 8371bfc819f..1890fd512f2 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus @@ -21,7 +21,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ openjdk-11-jdk \ openjdk-17-jdk \ -&& sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 +&& update-java-alternatives -s java-1.11.0-openjdk-amd64 # Known good combinations of JTReg and the JDK appear at https://builds.shipilev.net/jtreg/ . diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20 b/checker/bin-devel/Dockerfile-ubuntu-jdk20 index 64678b52c55..f720244f95a 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk20 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk20 @@ -49,7 +49,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ jtreg6 -# Maven 3.8.7 is the default on Ubuntu 23.04. +# Maven 3.8.7 is the default on Ubuntu 23.04, so the below is not needed. # (Don't try to use a variable here for the Maven version.) # RUN export DEBIAN_FRONTEND=noninteractive \ # && wget https://mirrors.sonic.net/apache/maven/maven-3/3.9.2/binaries/apache-maven-3.9.2-bin.tar.gz \ From f51868408202faa3623f7841accfc5f5ce9941fc Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 08:40:38 -0700 Subject: [PATCH 27/91] Suppress "this-escape" warning --- .../org/checkerframework/javacutil/AnnotationMirrorMap.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java index b683b3862e6..9ee077101b7 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java @@ -38,7 +38,10 @@ public AnnotationMirrorMap() {} * * @param copy a map whose contents should be copied to the newly created map */ - @SuppressWarnings("nullness:method.invocation") // initialization in constructor + @SuppressWarnings({ + "nullness:method.invocation", // initialization in constructor + "this-escape" + }) public AnnotationMirrorMap(Map copy) { this(); this.putAll(copy); From 37da7e6d6feabb9060d29d46c66ca1d12e6402d5 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 09:04:17 -0700 Subject: [PATCH 28/91] Suppress "this-escape" warning --- .../org/checkerframework/javacutil/AnnotationMirrorSet.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java index a17a92871a2..c9117d317b4 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java @@ -52,6 +52,7 @@ public AnnotationMirrorSet() {} * * @param value the AnnotationMirror to put in the set */ + @SuppressWarnings("this-escape") public AnnotationMirrorSet(AnnotationMirror value) { this.add(value); } @@ -61,6 +62,7 @@ public AnnotationMirrorSet(AnnotationMirror value) { * * @param annos the AnnotationMirrors to put in the set */ + @SuppressWarnings("this-escape") public AnnotationMirrorSet(Collection annos) { this.addAll(annos); } From 47ee8857ce19a5cf98330c38edc7864ebfd51136 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 09:34:56 -0700 Subject: [PATCH 29/91] Suppress "this-escape" warning --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index b545f635c30..6b7d4ed00b3 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -378,6 +378,7 @@ public class CFGTranslationPhaseOne extends TreeScanner { * @param assumeAssertionsEnabled can assertions be assumed to be enabled? * @param env annotation processing environment containing type utilities */ + @SuppressWarnings("this-escape") public CFGTranslationPhaseOne( TreeBuilder treeBuilder, AnnotationProvider annotationProvider, From 53665c18c5d5aee463d7aa7acae06d93f88afe13 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 10:14:45 -0700 Subject: [PATCH 30/91] Suppress "this-escape" warning --- .../framework/test/AinferGeneratePerDirectoryTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java index 943d39f2421..afe00ff181f 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java @@ -23,6 +23,7 @@ public abstract class AinferGeneratePerDirectoryTest extends CheckerFrameworkWPI * @param testDir the path to the directory of test inputs * @param checkerOptions options to pass to the compiler when running tests */ + @SuppressWarnings("this-escape") protected AinferGeneratePerDirectoryTest( List testFiles, Class checker, From 2ff5a0402fdd4e5e8f0484c0f0ae8ce648d78859 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 10:27:15 -0700 Subject: [PATCH 31/91] Suppress "this-escape" warning --- .../checkerframework/framework/test/PerDirectorySuite.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java index 753d06c338a..af470e558e4 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java @@ -52,7 +52,10 @@ protected List getChildren() { * * @param klass the class whose tests to run */ - @SuppressWarnings("nullness") // JUnit needs to be annotated + @SuppressWarnings({ + "nullness", // JUnit needs to be annotated + "this-escape" + }) public PerDirectorySuite(Class klass) throws Throwable { super(klass, Collections.emptyList()); TestClass testClass = getTestClass(); From f7dca872ee389fa881fe62d73381e78dfca7341c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 10:53:12 -0700 Subject: [PATCH 32/91] Suppress "this-escape" warning --- .../org/checkerframework/framework/test/PerFileSuite.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java index 84db8ceb825..b177d3fe2c4 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java @@ -53,7 +53,10 @@ protected List getChildren() { * * @param klass the class whose tests to run */ - @SuppressWarnings("nullness") // JUnit needs to be annotated + @SuppressWarnings({ + "nullness", // JUnit needs to be annotated + "this-escape" + }) public PerFileSuite(Class klass) throws Throwable { super(klass, Collections.emptyList()); TestClass testClass = getTestClass(); From 0096386b2a720e6c0cb2c5b3ae3baf17038d4470 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 11:02:09 -0700 Subject: [PATCH 33/91] Suppress "this-escape" warning --- .../framework/test/TestConfigurationBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java b/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java index 88aca554778..386cb2ecfe8 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java @@ -208,6 +208,7 @@ public TestConfigurationBuilder() { * * @param initialConfig initial configuration for the newly-created builder */ + @SuppressWarnings("this-escape") public TestConfigurationBuilder(TestConfiguration initialConfig) { this.diagnosticFiles = new ArrayList<>(initialConfig.getDiagnosticFiles()); this.testSourceFiles = new ArrayList<>(initialConfig.getTestSourceFiles()); From 4ee9b7a2154ece02749365eb1e7b8b97f73ff597 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 11:11:52 -0700 Subject: [PATCH 34/91] Suppress warnings --- build.gradle | 18 ++++++++++++++---- .../ainfer/AinferTestAnnotatedTypeFactory.java | 6 ++++++ .../disbaruse/DisbarUseTypeFactory.java | 7 +++++++ ...herCompoundCheckerAnnotatedTypeFactory.java | 6 ++++++ .../CompoundCheckerAnnotatedTypeFactory.java | 1 + .../FlowExpressionAnnotatedTypeFactory.java | 6 ++++++ .../h1h2checker/H1H2AnnotatedTypeFactory.java | 6 ++++++ .../TestAccumulationAnnotatedTypeFactory.java | 1 + 8 files changed, 47 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 441865e6624..58165754aae 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ ext { // This call to `isCompatibleWith` causes a Gradle run-time failure: "No signature of method". // isJava17compatible = JavaVersion.isCompatibleWith(JavaVersion.VERSION_17) isJava17orHigher = JavaVersion.current() >= JavaVersion.VERSION_17 + isJava21orHigher = JavaVersion.current() >= JavaVersion.VERSION_21 // As of 2023-09-08, delombok doesn't yet support JDK 21; see https://projectlombok.org/changelog . skipDelombok = JavaVersion.current() == JavaVersion.VERSION_21 @@ -200,6 +201,10 @@ allprojects { doNotFormat += ['**/java17/', '**/*record*/'] } + if (!isJava21orHigher) { + doNotFormat += ['**/java21/'] + } + format 'misc', { // define the files to apply `misc` to target '*.md', '*.tex', '.gitignore', 'Makefile' @@ -354,13 +359,18 @@ allprojects { options.failOnError = true options.deprecation = true + // -options: To not get a warning about missing bootstrap classpath (when using Java 9 and `-source 8`). + // -fallthrough: Don't check fallthroughs. Instead, use Error Prone. Its + // warnings are suppressible with a "// fall through" comment. + String lint = '-Xlint:-options,-fallthrough' + if(isJava21orHigher) { + // TODO: Ignore these for now, we may want to review and suppress each one later. + lint +=',-this-escape' + } options.compilerArgs += [ '-g', '-Werror', - // -options: To not get a warning about missing bootstrap classpath (when using Java 9 and `-source 8`). - // -fallthrough: Don't check fallthroughs. Instead, use Error Prone. Its - // warnings are suppressible with a "// fall through" comment. - '-Xlint:-options,-fallthrough', + lint, '-Xlint', ] diff --git a/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java b/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java index 3c61c81f7c7..85483bb16fa 100644 --- a/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java +++ b/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java @@ -89,6 +89,12 @@ public class AinferTestAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { private final ExecutableElement siblingWithFieldsValue2Element = TreeUtils.getMethod(AinferSiblingWithFields.class, "value2", 0, processingEnv); + /** + * Creates an AinferTestAnnotatedTypeFactory. + * + * @param checker the checker + */ + @SuppressWarnings("this-escape") public AinferTestAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); // Support a declaration annotation that has the same meaning as @Sibling1, to test that the diff --git a/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java b/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java index c3812c3ebaf..4581cd0e508 100644 --- a/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java +++ b/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java @@ -9,7 +9,14 @@ import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; +/** The type factory for forbidding use of the DisbarUse type. */ public class DisbarUseTypeFactory extends BaseAnnotatedTypeFactory { + /** + * Creates a new DisbarUseTypeFactory. + * + * @param checker the checker + */ + @SuppressWarnings("this-escape") public DisbarUseTypeFactory(BaseTypeChecker checker) { super(checker); postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java index 4ea364c00f7..f79d6b3eb4c 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java @@ -18,6 +18,12 @@ public class AnotherCompoundCheckerAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { + /** + * Creates a new AnotherCompoundCheckerAnnotatedTypeFactory. + * + * @param checker the checker + */ + @SuppressWarnings("this-escape") public AnotherCompoundCheckerAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java index ece07ad4c76..9ddcdaf2b60 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java @@ -18,6 +18,7 @@ public class CompoundCheckerAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { + @SuppressWarnings("this-escape") public CompoundCheckerAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java index 2bc11ff7671..89bc9f11533 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java @@ -25,6 +25,12 @@ public class FlowExpressionAnnotatedTypeFactory extends BaseAnnotatedTypeFactory ExecutableElement flowExpValueElement = TreeUtils.getMethod(FlowExp.class, "value", 0, processingEnv); + /** + * Creates a new FlowExpressionAnnotatedTypeFactory. + * + * @param checker the checker + */ + @SuppressWarnings("this-escape") public FlowExpressionAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); TOP = AnnotationBuilder.fromClass(elements, FETop.class); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java index de9183dde59..8329915a56d 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java @@ -25,6 +25,12 @@ public class H1H2AnnotatedTypeFactory extends BaseAnnotatedTypeFactory { AnnotationMirror H1S2; + /** + * Creates a new H1H2AnnotatedTypeFactory. + * + * @param checker the checker + */ + @SuppressWarnings("this-escape") public H1H2AnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java index 34fefdf0711..0fe7b8345b3 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java @@ -24,6 +24,7 @@ public class TestAccumulationAnnotatedTypeFactory extends AccumulationAnnotatedT * * @param checker the checker */ + @SuppressWarnings("this-escape") public TestAccumulationAnnotatedTypeFactory(BaseTypeChecker checker) { super( checker, From 59896b3b104a0c3e24b2f3010436497f19a6f570 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 11:46:34 -0700 Subject: [PATCH 35/91] Add Javadoc --- .../org/checkerframework/framework/test/PerDirectorySuite.java | 1 + .../java/org/checkerframework/framework/test/PerFileSuite.java | 1 + 2 files changed, 2 insertions(+) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java index af470e558e4..b7534e21c88 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java @@ -51,6 +51,7 @@ protected List getChildren() { * Only called reflectively. Do not use programmatically. * * @param klass the class whose tests to run + * @throws Throwable if there is trouble */ @SuppressWarnings({ "nullness", // JUnit needs to be annotated diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java index b177d3fe2c4..7a94d78c3c6 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java @@ -52,6 +52,7 @@ protected List getChildren() { * Only called reflectively. Do not use programmatically. * * @param klass the class whose tests to run + * @throws Throwable if there is trouble */ @SuppressWarnings({ "nullness", // JUnit needs to be annotated From f9294a5b170175b610e6004cce05815c450f955c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 16:42:45 -0700 Subject: [PATCH 36/91] Add --add-exports --- build.gradle | 6 ++++++ docs/examples/MavenExample/pom.xml | 1 + docs/manual/external-tools.tex | 5 +++++ .../org/checkerframework/framework/util/CheckerMain.java | 2 ++ 4 files changed, 14 insertions(+) diff --git a/build.gradle b/build.gradle index 58165754aae..c2719fcffd3 100644 --- a/build.gradle +++ b/build.gradle @@ -176,6 +176,8 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', + '--add-exports', + 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', 'jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', @@ -293,6 +295,7 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) + options.addBooleanOption('--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. @@ -341,6 +344,8 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', + '--add-exports', + 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', ] // This is equivalent to writing "exports jdk.compiler/... to ALL-UNNAMED" in the // module-info.java of jdk.compiler, so corresponding --add-opens are only required for @@ -835,6 +840,7 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', + '-javacoptions:--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index 61b3b774fce..659dbc07e95 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,6 +158,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 15f5a1401d3..5dfec62736c 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,6 +651,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ +-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -705,6 +706,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1122,6 +1124,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1402,6 +1405,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1423,6 +1427,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index b6fd649a02e..5dee72d5d24 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -462,6 +462,8 @@ public List getExecArguments() { "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports", + "jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. "--add-opens", From 142b85f483113bfff5b738ba4a18b0b333be701a Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 16:59:12 -0700 Subject: [PATCH 37/91] Fix command-line syntax --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c2719fcffd3..109adb17f70 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,7 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) - options.addBooleanOption('--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) + options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. From d47e42a442fb4fe623313250f1ca4158dc170e9a Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 17 Sep 2023 18:06:28 -0700 Subject: [PATCH 38/91] Fix typo --- build.gradle | 8 ++++---- docs/examples/MavenExample/pom.xml | 2 +- docs/manual/external-tools.tex | 10 +++++----- .../checkerframework/framework/util/CheckerMain.java | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 109adb17f70..deb2b1eb2c0 100644 --- a/build.gradle +++ b/build.gradle @@ -177,7 +177,7 @@ allprojects { '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', '--add-exports', - 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', + 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', 'jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', @@ -295,7 +295,7 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) - options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) + options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. @@ -345,7 +345,7 @@ allprojects { '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', '--add-exports', - 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', + 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', ] // This is equivalent to writing "exports jdk.compiler/... to ALL-UNNAMED" in the // module-info.java of jdk.compiler, so corresponding --add-opens are only required for @@ -840,7 +840,7 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', - '-javacoptions:--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', + '-javacoptions:--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index 659dbc07e95..14e50ea3023 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,7 +158,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 5dfec62736c..6beef352e16 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,7 +651,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ --J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED \ +-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -706,7 +706,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1124,7 +1124,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1405,7 +1405,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1427,7 +1427,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index 5dee72d5d24..2915aed9203 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -463,7 +463,7 @@ public List getExecArguments() { "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", "--add-exports", - "jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", + "jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. "--add-opens", From b7ee23cf694983f24edd559c21123e16d7bc1dbe Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 08:29:31 -0700 Subject: [PATCH 39/91] Use --add-opens because of reflective access. --- build.gradle | 6 ++++-- docs/examples/MavenExample/pom.xml | 2 +- docs/manual/external-tools.tex | 10 +++++----- .../checkerframework/framework/util/CheckerMain.java | 4 +++- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index deb2b1eb2c0..13e412ecdec 100644 --- a/build.gradle +++ b/build.gradle @@ -176,7 +176,9 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', - '--add-exports', + // Required because the Checker Framework reflectively accesses private members. + // See org.checkerframework.afu.scenelib.util.CommandLineUtils.parseCommandLine. + '--add-opens', 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', @@ -840,7 +842,6 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', - '-javacoptions:--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', @@ -851,6 +852,7 @@ subprojects { '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', + '-vmoptions:--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', ] if (project.name.is('framework')) { // Do not check for the annotated JDK diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index 14e50ea3023..bd974614311 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,7 +158,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 6beef352e16..2fed2d1ac9b 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,7 +651,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ --J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ +-J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -706,7 +706,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1124,7 +1124,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1405,7 +1405,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1427,7 +1427,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index 2915aed9203..56d98b7ce2d 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -462,7 +462,9 @@ public List getExecArguments() { "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "--add-exports", + // Required because the Checker Framework reflectively accesses private + // members in jdk.internal.opt. + "--add-opens", "jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. From 15a94f17d542863b830e32768323434a03080f15 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 10:25:48 -0700 Subject: [PATCH 40/91] Revert "Use --add-opens because of reflective access." This reverts commit b7ee23cf694983f24edd559c21123e16d7bc1dbe. --- build.gradle | 6 ++---- docs/examples/MavenExample/pom.xml | 2 +- docs/manual/external-tools.tex | 10 +++++----- .../checkerframework/framework/util/CheckerMain.java | 4 +--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index 13e412ecdec..deb2b1eb2c0 100644 --- a/build.gradle +++ b/build.gradle @@ -176,9 +176,7 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', - // Required because the Checker Framework reflectively accesses private members. - // See org.checkerframework.afu.scenelib.util.CommandLineUtils.parseCommandLine. - '--add-opens', + '--add-exports', 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', @@ -842,6 +840,7 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', + '-javacoptions:--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', @@ -852,7 +851,6 @@ subprojects { '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', - '-vmoptions:--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', ] if (project.name.is('framework')) { // Do not check for the annotated JDK diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index bd974614311..14e50ea3023 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,7 +158,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 2fed2d1ac9b..6beef352e16 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,7 +651,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ --J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ +-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -706,7 +706,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ - -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1124,7 +1124,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1405,7 +1405,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1427,7 +1427,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-opens=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index 56d98b7ce2d..2915aed9203 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -462,9 +462,7 @@ public List getExecArguments() { "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - // Required because the Checker Framework reflectively accesses private - // members in jdk.internal.opt. - "--add-opens", + "--add-exports", "jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. From 554068f8d473036703a751e02875e2298d42bdfd Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 10:28:19 -0700 Subject: [PATCH 41/91] Revert "Fix typo" This reverts commit d47e42a442fb4fe623313250f1ca4158dc170e9a. --- build.gradle | 8 ++++---- docs/examples/MavenExample/pom.xml | 2 +- docs/manual/external-tools.tex | 10 +++++----- .../checkerframework/framework/util/CheckerMain.java | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index deb2b1eb2c0..109adb17f70 100644 --- a/build.gradle +++ b/build.gradle @@ -177,7 +177,7 @@ allprojects { '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', '--add-exports', - 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', + 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', 'jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', @@ -295,7 +295,7 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) - options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', true) + options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. @@ -345,7 +345,7 @@ allprojects { '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', '--add-exports', - 'jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', + 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', ] // This is equivalent to writing "exports jdk.compiler/... to ALL-UNNAMED" in the // module-info.java of jdk.compiler, so corresponding --add-opens are only required for @@ -840,7 +840,7 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', - '-javacoptions:--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', + '-javacoptions:--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index 14e50ea3023..659dbc07e95 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,7 +158,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 6beef352e16..5dfec62736c 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,7 +651,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ --J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED \ +-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -706,7 +706,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1124,7 +1124,7 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED + -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1405,7 +1405,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1427,7 +1427,7 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index 2915aed9203..5dee72d5d24 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -463,7 +463,7 @@ public List getExecArguments() { "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", "--add-exports", - "jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. "--add-opens", From daafb18b5da72052dff89bea7df5952f26dd333d Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 10:28:34 -0700 Subject: [PATCH 42/91] Revert "Fix command-line syntax" This reverts commit 142b85f483113bfff5b738ba4a18b0b333be701a. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 109adb17f70..c2719fcffd3 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,7 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) - options.addBooleanOption('-add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) + options.addBooleanOption('--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. From 0e533e0cfceca82eac89563ffb3d0fdbb2091554 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 10:29:05 -0700 Subject: [PATCH 43/91] Revert "Add --add-exports" This reverts commit f9294a5b170175b610e6004cce05815c450f955c. --- build.gradle | 6 ------ docs/examples/MavenExample/pom.xml | 1 - docs/manual/external-tools.tex | 5 ----- .../org/checkerframework/framework/util/CheckerMain.java | 2 -- 4 files changed, 14 deletions(-) diff --git a/build.gradle b/build.gradle index c2719fcffd3..58165754aae 100644 --- a/build.gradle +++ b/build.gradle @@ -176,8 +176,6 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', - '--add-exports', - 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', // Required because the Checker Framework reflectively accesses private members in com.sun.tools.javac.comp. '--add-opens', 'jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', @@ -295,7 +293,6 @@ allprojects { options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', true) options.addBooleanOption('-add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', true) - options.addBooleanOption('--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', true) // "-Xwerror" requires Javadoc everywhere. Currently, CI jobs require Javadoc only // on changed lines. Enable -Xwerror in the future when all Javadoc exists. @@ -344,8 +341,6 @@ allprojects { 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports', 'jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', - '--add-exports', - 'jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', ] // This is equivalent to writing "exports jdk.compiler/... to ALL-UNNAMED" in the // module-info.java of jdk.compiler, so corresponding --add-opens are only required for @@ -840,7 +835,6 @@ subprojects { // uses the jdk.jdeps module. '-javacoptions:--add-modules jdk.jdeps', '-javacoptions:--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', - '-javacoptions:--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', '-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', diff --git a/docs/examples/MavenExample/pom.xml b/docs/examples/MavenExample/pom.xml index 659dbc07e95..61b3b774fce 100644 --- a/docs/examples/MavenExample/pom.xml +++ b/docs/examples/MavenExample/pom.xml @@ -158,7 +158,6 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/docs/manual/external-tools.tex b/docs/manual/external-tools.tex index 5dfec62736c..15f5a1401d3 100644 --- a/docs/manual/external-tools.tex +++ b/docs/manual/external-tools.tex @@ -651,7 +651,6 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ --J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -706,7 +705,6 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ @@ -1124,7 +1122,6 @@ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -1405,7 +1402,6 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", @@ -1427,7 +1423,6 @@ "-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "-J--add-exports=jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", "-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar", "--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar", diff --git a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java index 5dee72d5d24..b6fd649a02e 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java +++ b/framework/src/main/java/org/checkerframework/framework/util/CheckerMain.java @@ -462,8 +462,6 @@ public List getExecArguments() { "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - "--add-exports", - "jdk.internal.opt/jdk.intenal.opt=ALL-UNNAMED", // Required because the Checker Framework reflectively accesses private // members in com.sun.tools.javac.comp. "--add-opens", From 22f43597df9b9eddf033bbce0af28d6a85f1b558 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 18 Sep 2023 12:07:44 -0700 Subject: [PATCH 44/91] Checkpoint. --- .../cfg/builder/CFGTranslationPhaseOne.java | 13 ++++++++++--- .../framework/flow/CFAbstractTransfer.java | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 235a52d36cb..1f4f654c1be 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -3802,10 +3802,17 @@ public Node visitTypeParameter(TypeParameterTree tree, Void p) { @Override public Node visitInstanceOf(InstanceOfTree tree, Void p) { Node operand = scan(tree.getExpression(), p); - TypeMirror refType = TreeUtils.typeOf(tree.getType()); Tree binding = TreeUtils.instanceOfTreeGetPattern(tree); - LocalVariableNode bindingNode = - (LocalVariableNode) ((binding == null) ? null : scan(binding, p)); + TypeMirror refType; + LocalVariableNode bindingNode; + if (binding != null) { + bindingNode = (LocalVariableNode) scan(binding, p); + // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node instead. + refType = bindingNode.getType(); + } else { + refType = TreeUtils.typeOf(tree.getType()); + bindingNode = null; + } InstanceOfNode node = new InstanceOfNode(tree, operand, bindingNode, refType, types); extendWithNode(node); diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index e63ee1b0a67..2d46bd020ee 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -971,6 +971,16 @@ public TransferResult visitMethodInvocation( @Override public TransferResult visitInstanceOf(InstanceOfNode node, TransferInput in) { TransferResult result = super.visitInstanceOf(node, in); + if (node.getBindingVariable() != null) { + JavaExpression expr = JavaExpression.fromNode(node.getBindingVariable()); + AnnotatedTypeMirror expType = + analysis.atypeFactory.getAnnotatedType(node.getTree().getExpression()); + for (AnnotationMirror anno : expType.getPrimaryAnnotations()) { + in.getRegularStore().insertOrRefine(expr, anno); + } + return result; + } + // The "reference type" is the type after "instanceof". Tree refTypeTree = node.getTree().getType(); if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) { @@ -987,15 +997,6 @@ public TransferResult visitInstanceOf(InstanceOfNode node, TransferInput(result.getResultValue(), in.getRegularStore()); } } - // TODO: Should this be an else if? - if (node.getBindingVariable() != null) { - JavaExpression expr = JavaExpression.fromNode(node.getBindingVariable()); - AnnotatedTypeMirror expType = - analysis.atypeFactory.getAnnotatedType(node.getTree().getExpression()); - for (AnnotationMirror anno : expType.getPrimaryAnnotations()) { - in.getRegularStore().insertOrRefine(expr, anno); - } - } return result; } From ad2d8a88f03807ea144ac74e6d65058d36020232 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 19 Sep 2023 08:27:18 -0700 Subject: [PATCH 45/91] Revert to gradle 8.3 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 10db5ee7e8c..ac72c34e8ac 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.4-20230909005132+0000-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1a532..1aa94a42690 100755 --- a/gradlew +++ b/gradlew @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From 2817a6a997ba54b50ba687c05375217088d7b2e7 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 19 Sep 2023 09:30:16 -0700 Subject: [PATCH 46/91] Revert suppressing this-escape warnings. --- .../testchecker/ainfer/AinferTestAnnotatedTypeFactory.java | 6 ------ .../testchecker/disbaruse/DisbarUseTypeFactory.java | 7 ------- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 1 - .../framework/test/AinferGeneratePerDirectoryTest.java | 1 - .../checkerframework/framework/test/PerDirectorySuite.java | 6 +----- .../org/checkerframework/framework/test/PerFileSuite.java | 6 +----- .../framework/test/TestConfigurationBuilder.java | 1 - .../AnotherCompoundCheckerAnnotatedTypeFactory.java | 6 ------ .../compound/CompoundCheckerAnnotatedTypeFactory.java | 1 - .../flowexpression/FlowExpressionAnnotatedTypeFactory.java | 6 ------ .../testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java | 6 ------ .../TestAccumulationAnnotatedTypeFactory.java | 1 - .../checkerframework/javacutil/AnnotationMirrorMap.java | 5 +---- .../checkerframework/javacutil/AnnotationMirrorSet.java | 2 -- 14 files changed, 3 insertions(+), 52 deletions(-) diff --git a/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java b/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java index 85483bb16fa..3c61c81f7c7 100644 --- a/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java +++ b/checker/src/test/java/org/checkerframework/checker/testchecker/ainfer/AinferTestAnnotatedTypeFactory.java @@ -89,12 +89,6 @@ public class AinferTestAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { private final ExecutableElement siblingWithFieldsValue2Element = TreeUtils.getMethod(AinferSiblingWithFields.class, "value2", 0, processingEnv); - /** - * Creates an AinferTestAnnotatedTypeFactory. - * - * @param checker the checker - */ - @SuppressWarnings("this-escape") public AinferTestAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); // Support a declaration annotation that has the same meaning as @Sibling1, to test that the diff --git a/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java b/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java index 4581cd0e508..c3812c3ebaf 100644 --- a/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java +++ b/checker/src/test/java/org/checkerframework/checker/testchecker/disbaruse/DisbarUseTypeFactory.java @@ -9,14 +9,7 @@ import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; -/** The type factory for forbidding use of the DisbarUse type. */ public class DisbarUseTypeFactory extends BaseAnnotatedTypeFactory { - /** - * Creates a new DisbarUseTypeFactory. - * - * @param checker the checker - */ - @SuppressWarnings("this-escape") public DisbarUseTypeFactory(BaseTypeChecker checker) { super(checker); postInit(); diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 6b7d4ed00b3..b545f635c30 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -378,7 +378,6 @@ public class CFGTranslationPhaseOne extends TreeScanner { * @param assumeAssertionsEnabled can assertions be assumed to be enabled? * @param env annotation processing environment containing type utilities */ - @SuppressWarnings("this-escape") public CFGTranslationPhaseOne( TreeBuilder treeBuilder, AnnotationProvider annotationProvider, diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java index afe00ff181f..943d39f2421 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/AinferGeneratePerDirectoryTest.java @@ -23,7 +23,6 @@ public abstract class AinferGeneratePerDirectoryTest extends CheckerFrameworkWPI * @param testDir the path to the directory of test inputs * @param checkerOptions options to pass to the compiler when running tests */ - @SuppressWarnings("this-escape") protected AinferGeneratePerDirectoryTest( List testFiles, Class checker, diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java index b7534e21c88..753d06c338a 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerDirectorySuite.java @@ -51,12 +51,8 @@ protected List getChildren() { * Only called reflectively. Do not use programmatically. * * @param klass the class whose tests to run - * @throws Throwable if there is trouble */ - @SuppressWarnings({ - "nullness", // JUnit needs to be annotated - "this-escape" - }) + @SuppressWarnings("nullness") // JUnit needs to be annotated public PerDirectorySuite(Class klass) throws Throwable { super(klass, Collections.emptyList()); TestClass testClass = getTestClass(); diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java index 7a94d78c3c6..84db8ceb825 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/PerFileSuite.java @@ -52,12 +52,8 @@ protected List getChildren() { * Only called reflectively. Do not use programmatically. * * @param klass the class whose tests to run - * @throws Throwable if there is trouble */ - @SuppressWarnings({ - "nullness", // JUnit needs to be annotated - "this-escape" - }) + @SuppressWarnings("nullness") // JUnit needs to be annotated public PerFileSuite(Class klass) throws Throwable { super(klass, Collections.emptyList()); TestClass testClass = getTestClass(); diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java b/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java index 386cb2ecfe8..88aca554778 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/TestConfigurationBuilder.java @@ -208,7 +208,6 @@ public TestConfigurationBuilder() { * * @param initialConfig initial configuration for the newly-created builder */ - @SuppressWarnings("this-escape") public TestConfigurationBuilder(TestConfiguration initialConfig) { this.diagnosticFiles = new ArrayList<>(initialConfig.getDiagnosticFiles()); this.testSourceFiles = new ArrayList<>(initialConfig.getTestSourceFiles()); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java index f79d6b3eb4c..4ea364c00f7 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/AnotherCompoundCheckerAnnotatedTypeFactory.java @@ -18,12 +18,6 @@ public class AnotherCompoundCheckerAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { - /** - * Creates a new AnotherCompoundCheckerAnnotatedTypeFactory. - * - * @param checker the checker - */ - @SuppressWarnings("this-escape") public AnotherCompoundCheckerAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java index 9ddcdaf2b60..ece07ad4c76 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/compound/CompoundCheckerAnnotatedTypeFactory.java @@ -18,7 +18,6 @@ public class CompoundCheckerAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { - @SuppressWarnings("this-escape") public CompoundCheckerAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java index 89bc9f11533..2bc11ff7671 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/flowexpression/FlowExpressionAnnotatedTypeFactory.java @@ -25,12 +25,6 @@ public class FlowExpressionAnnotatedTypeFactory extends BaseAnnotatedTypeFactory ExecutableElement flowExpValueElement = TreeUtils.getMethod(FlowExp.class, "value", 0, processingEnv); - /** - * Creates a new FlowExpressionAnnotatedTypeFactory. - * - * @param checker the checker - */ - @SuppressWarnings("this-escape") public FlowExpressionAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); TOP = AnnotationBuilder.fromClass(elements, FETop.class); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java index 8329915a56d..de9183dde59 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/h1h2checker/H1H2AnnotatedTypeFactory.java @@ -25,12 +25,6 @@ public class H1H2AnnotatedTypeFactory extends BaseAnnotatedTypeFactory { AnnotationMirror H1S2; - /** - * Creates a new H1H2AnnotatedTypeFactory. - * - * @param checker the checker - */ - @SuppressWarnings("this-escape") public H1H2AnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); this.postInit(); diff --git a/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java b/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java index 0fe7b8345b3..34fefdf0711 100644 --- a/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java +++ b/framework/src/test/java/org/checkerframework/framework/testchecker/testaccumulation/TestAccumulationAnnotatedTypeFactory.java @@ -24,7 +24,6 @@ public class TestAccumulationAnnotatedTypeFactory extends AccumulationAnnotatedT * * @param checker the checker */ - @SuppressWarnings("this-escape") public TestAccumulationAnnotatedTypeFactory(BaseTypeChecker checker) { super( checker, diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java index 9ee077101b7..b683b3862e6 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorMap.java @@ -38,10 +38,7 @@ public AnnotationMirrorMap() {} * * @param copy a map whose contents should be copied to the newly created map */ - @SuppressWarnings({ - "nullness:method.invocation", // initialization in constructor - "this-escape" - }) + @SuppressWarnings("nullness:method.invocation") // initialization in constructor public AnnotationMirrorMap(Map copy) { this(); this.putAll(copy); diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java index c9117d317b4..a17a92871a2 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationMirrorSet.java @@ -52,7 +52,6 @@ public AnnotationMirrorSet() {} * * @param value the AnnotationMirror to put in the set */ - @SuppressWarnings("this-escape") public AnnotationMirrorSet(AnnotationMirror value) { this.add(value); } @@ -62,7 +61,6 @@ public AnnotationMirrorSet(AnnotationMirror value) { * * @param annos the AnnotationMirrors to put in the set */ - @SuppressWarnings("this-escape") public AnnotationMirrorSet(Collection annos) { this.addAll(annos); } From ef231f3db17e519c8ed0f9a13e730196e347d056 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 19 Sep 2023 10:38:43 -0700 Subject: [PATCH 47/91] Doesn't crash on JEP 440. --- .../cfg/builder/CFGTranslationPhaseOne.java | 27 ++++++++++++++++--- .../common/basetype/BaseTypeVisitor.java | 18 ++++++++----- .../framework/flow/CFAbstractTransfer.java | 2 +- .../framework/type/TypeFromMemberVisitor.java | 9 +++++-- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 1f4f654c1be..0a04827c492 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -549,6 +549,8 @@ public Node scan(Tree tree, Void p) { return visitSwitchExpression17(tree, p); case "YIELD": return visitYield17(tree, p); + case "DECONSTRUCTION_PATTERN": + return visitDeconstructionPattern21(tree, p); default: // fall through to generic behavior } @@ -607,6 +609,18 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { return varNode; } + /** + * Visit a DeconstructionPatternTree + * + * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree to be + * backward-compatible + * @param p parameter + * @return the result of visiting the tree + */ + public Node visitDeconstructionPattern21(Tree deconstructionPatternTree, Void p) { + throw new RuntimeException("Not implemented"); + } + /* --------------------------------------------------------- */ /* Nodes and Labels Management */ /* --------------------------------------------------------- */ @@ -3805,10 +3819,17 @@ public Node visitInstanceOf(InstanceOfTree tree, Void p) { Tree binding = TreeUtils.instanceOfTreeGetPattern(tree); TypeMirror refType; LocalVariableNode bindingNode; + if (binding != null) { - bindingNode = (LocalVariableNode) scan(binding, p); - // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node instead. - refType = bindingNode.getType(); + if (!binding.getKind().name().contentEquals("DECONSTRUCTION_PATTERN")) { + bindingNode = (LocalVariableNode) scan(binding, p); + // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node + // instead. + refType = bindingNode.getType(); + } else { + bindingNode = null; + refType = TreeUtils.typeOf(binding); + } } else { refType = TreeUtils.typeOf(tree.getType()); bindingNode = null; diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index fc0fc7a3af2..467de41deb6 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -2577,14 +2577,18 @@ public Void visitInstanceOf(InstanceOfTree tree, Void p) { // The "reference type" is the type after "instanceof". Tree patternTree = TreeUtils.instanceOfTreeGetPattern(tree); if (patternTree != null) { - VariableTree variableTree = TreeUtils.bindingPatternTreeGetVariable(patternTree); - validateTypeOf(variableTree); - if (variableTree.getModifiers() != null) { - AnnotatedTypeMirror variableType = atypeFactory.getAnnotatedType(variableTree); - AnnotatedTypeMirror expType = atypeFactory.getAnnotatedType(tree.getExpression()); - if (!isTypeCastSafe(variableType, expType)) { - checker.reportWarning(tree, "instanceof.pattern.unsafe", expType, variableTree); + if (patternTree.getKind().name().equals("BINDING_PATTERN")) { + VariableTree variableTree = TreeUtils.bindingPatternTreeGetVariable(patternTree); + validateTypeOf(variableTree); + if (variableTree.getModifiers() != null) { + AnnotatedTypeMirror variableType = atypeFactory.getAnnotatedType(variableTree); + AnnotatedTypeMirror expType = atypeFactory.getAnnotatedType(tree.getExpression()); + if (!isTypeCastSafe(variableType, expType)) { + checker.reportWarning(tree, "instanceof.pattern.unsafe", expType, variableTree); + } } + } else { + // TODO: } } else { Tree refTypeTree = tree.getType(); diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index 2d46bd020ee..8bd178f492a 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -983,7 +983,7 @@ public TransferResult visitInstanceOf(InstanceOfNode node, TransferInput Date: Tue, 19 Sep 2023 10:44:39 -0700 Subject: [PATCH 48/91] Don't run jdk20 jobs. --- azure-pipelines.yml | 204 ++++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2083fd8cacc..b183412cc8a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -26,7 +26,7 @@ jobs: - typecheck_part1_jdk21 - typecheck_part2_jdk21 - misc_jdk21 - - misc_jdk20 + # - misc_jdk20 pool: vmImage: 'ubuntu-latest' steps: @@ -67,18 +67,18 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-junit.sh displayName: test-cftests-junit.sh -- job: junit_jdk20 - dependsOn: - - canary_jobs - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - timeoutInMinutes: 70 - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-junit.sh - displayName: test-cftests-junit.sh +# - job: junit_jdk20 +# dependsOn: +# - canary_jobs +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# timeoutInMinutes: 70 +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-cftests-junit.sh +# displayName: test-cftests-junit.sh - job: nonjunit_jdk11 dependsOn: - canary_jobs @@ -112,17 +112,17 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-nonjunit.sh displayName: test-cftests-nonjunit.sh -- job: nonjunit_jdk20 - dependsOn: - - canary_jobs - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-nonjunit.sh - displayName: test-cftests-nonjunit.sh +# - job: nonjunit_jdk20 +# dependsOn: +# - canary_jobs +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-cftests-nonjunit.sh +# displayName: test-cftests-nonjunit.sh # Sometimes one of the invocations of wpi-many in `./gradlew wpiManyTest` # takes much longer to complete than normal, and this Azure job times out. # When there is a timeout, one cannot examine wpi or wpi-many logs. @@ -176,20 +176,20 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-inference-part2.sh displayName: test-cftests-inference-part2.sh -- job: inference_jdk20 - dependsOn: - - canary_jobs - - inference_part1_jdk21 - - inference_part2_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - timeoutInMinutes: 90 - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-inference.sh - displayName: test-cftests-inference.sh +# - job: inference_jdk20 +# dependsOn: +# - canary_jobs +# - inference_part1_jdk21 +# - inference_part2_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# timeoutInMinutes: 90 +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-cftests-inference.sh +# displayName: test-cftests-inference.sh # Unlimited fetchDepth for misc_jobs, because of need to make contributors.tex - job: misc_jdk11 dependsOn: @@ -224,18 +224,18 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-misc.sh displayName: test-misc.sh -- job: misc_jdk20 - dependsOn: - # - canary_jobs - - misc_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20-plus:latest - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-misc.sh - displayName: test-misc.sh +# - job: misc_jdk20 +# dependsOn: +# # - canary_jobs +# - misc_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20-plus:latest +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-misc.sh +# displayName: test-misc.sh - job: typecheck_jdk11 dependsOn: - canary_jobs @@ -281,19 +281,19 @@ jobs: fetchDepth: 1000 - bash: ./checker/bin-devel/test-typecheck-part2.sh displayName: test-typecheck-part2.sh -- job: typecheck_jdk20 - dependsOn: - - canary_jobs - - typecheck_part1_jdk21 - - typecheck_part2_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20-plus:latest - steps: - - checkout: self - fetchDepth: 1000 - - bash: ./checker/bin-devel/test-typecheck.sh - displayName: test-typecheck.sh +# - job: typecheck_jdk20 +# dependsOn: +# - canary_jobs +# - typecheck_part1_jdk21 +# - typecheck_part2_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20-plus:latest +# steps: +# - checkout: self +# fetchDepth: 1000 +# - bash: ./checker/bin-devel/test-typecheck.sh +# displayName: test-typecheck.sh - job: daikon_jdk11 dependsOn: - canary_jobs @@ -346,20 +346,20 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-daikon.sh displayName: test-daikon-part2.sh -- job: daikon_jdk20 - dependsOn: - - canary_jobs - - daikon_part1_jdk21 - - daikon_part2_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - timeoutInMinutes: 80 - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-daikon.sh - displayName: test-daikon.sh +# - job: daikon_jdk20 +# dependsOn: +# - canary_jobs +# - daikon_part1_jdk21 +# - daikon_part2_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# timeoutInMinutes: 80 +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-daikon.sh +# displayName: test-daikon.sh - job: guava_jdk11 dependsOn: - canary_jobs @@ -403,19 +403,19 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-guava.sh displayName: test-guava.sh -- job: guava_jdk20 - dependsOn: - - canary_jobs - - guava_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - timeoutInMinutes: 60 - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-guava.sh - displayName: test-guava.sh +# - job: guava_jdk20 +# dependsOn: +# - canary_jobs +# - guava_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# timeoutInMinutes: 60 +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-guava.sh +# displayName: test-guava.sh - job: plume_lib_jdk11 dependsOn: - canary_jobs @@ -451,18 +451,18 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-plume-lib.sh displayName: test-plume-lib.sh -- job: plume_lib_jdk20 - dependsOn: - - canary_jobs - - plume_lib_jdk21 - pool: - vmImage: 'ubuntu-latest' - container: mdernst/cf-ubuntu-jdk20:latest - steps: - - checkout: self - fetchDepth: 25 - - bash: ./checker/bin-devel/test-plume-lib.sh - displayName: test-plume-lib.sh +# - job: plume_lib_jdk20 +# dependsOn: +# - canary_jobs +# - plume_lib_jdk21 +# pool: +# vmImage: 'ubuntu-latest' +# container: mdernst/cf-ubuntu-jdk20:latest +# steps: +# - checkout: self +# fetchDepth: 25 +# - bash: ./checker/bin-devel/test-plume-lib.sh +# displayName: test-plume-lib.sh ## The downstream jobs are not currently needed because test-downstream.sh is empty. # - job: downstream_jdk11 # dependsOn: From b12e330a5e137436b4e296ef7f4196f0b3de0d11 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 20 Sep 2023 08:35:54 -0700 Subject: [PATCH 49/91] Can't format Java 21 language features. --- build.gradle | 5 ++++- framework/tests/all-systems/java21/Issue6173.java | 1 + framework/tests/all-systems/java21/JEP441.java | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 903dc11b578..dfb4d99c39c 100644 --- a/build.gradle +++ b/build.gradle @@ -201,7 +201,10 @@ allprojects { doNotFormat += ['**/java17/', '**/*record*/'] } - if (!isJava21orHigher) { + // TODO: reinstate. googlejavaformat can't parse new language features. + // (As of version 1.17.0.) + // if (!isJava21orHigher) { + if (true) { doNotFormat += ['**/java21/'] } diff --git a/framework/tests/all-systems/java21/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java index 890293f615c..78e6ff9a84f 100644 --- a/framework/tests/all-systems/java21/Issue6173.java +++ b/framework/tests/all-systems/java21/Issue6173.java @@ -1,4 +1,5 @@ // @below-java21-jdk-skip-test +// @infer-jaifs-skip-test public class Issue6173 { static Object toGroupByQueryWithExtractor2(GroupBy groupBy) { diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index ce6fbfae40d..de2454bc8c1 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -1,4 +1,5 @@ // @below-java21-jdk-skip-test +// @infer-jaifs-skip-test // These are examples copied from: // https://openjdk.org/jeps/441 public class JEP441 { From 6a7bda3add0925bb332e355352ee4a46a640beaf Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 20 Sep 2023 09:03:54 -0700 Subject: [PATCH 50/91] Skip more inference types. --- framework/tests/all-systems/java21/Issue6173.java | 1 + framework/tests/all-systems/java21/JEP441.java | 1 + 2 files changed, 2 insertions(+) diff --git a/framework/tests/all-systems/java21/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java index 78e6ff9a84f..0057d079820 100644 --- a/framework/tests/all-systems/java21/Issue6173.java +++ b/framework/tests/all-systems/java21/Issue6173.java @@ -1,5 +1,6 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test +// @infer-ajava-skip-test public class Issue6173 { static Object toGroupByQueryWithExtractor2(GroupBy groupBy) { diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index de2454bc8c1..a98486179d4 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -1,5 +1,6 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test +// @infer-ajava-skip-test // These are examples copied from: // https://openjdk.org/jeps/441 public class JEP441 { From a4a6025788344797197c00cffdfcb4aab25e1398 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 20 Sep 2023 09:08:19 -0700 Subject: [PATCH 51/91] Suppresswarnings. --- framework/tests/all-systems/java21/JEP441.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index a98486179d4..643ef137c3a 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -3,6 +3,8 @@ // @infer-ajava-skip-test // These are examples copied from: // https://openjdk.org/jeps/441 + +@SuppressWarnings("i18n") // true postives. public class JEP441 { // We enhance switch statements and expressions in four ways: From da0a1ff8e6e4760ab53830d23fff264e7384b0b5 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 20 Sep 2023 09:11:44 -0700 Subject: [PATCH 52/91] Add nonnull. --- .../main/java/org/checkerframework/javacutil/TreeUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 128fefcbb9f..7ff1183177e 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -2502,7 +2502,7 @@ public static ExpressionTree constantCaseLabelTreeGetConstantExpression( if (atLeastJava21) { try { @SuppressWarnings("nullness") - ExpressionTree ret = + @NonNull ExpressionTree ret = (ExpressionTree) CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION.invoke(constantCaseLabelTree); return ret; @@ -2537,7 +2537,7 @@ public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { if (atLeastJava21) { try { @SuppressWarnings("nullness") - Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); + @NonNull Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); return ret; } catch (IllegalAccessException | InvocationTargetException e) { throw new BugInCF( From a485f558672535482c3157910241f70f376c825c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 20 Sep 2023 11:58:30 -0700 Subject: [PATCH 53/91] Fix. --- .../cfg/builder/CFGTranslationPhaseOne.java | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 235a52d36cb..381824129f9 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2328,6 +2328,7 @@ private SwitchBuilder(Tree switchTree) { // Build CFG for the cases. int defaultIndex = -1; + boolean exhaustiveAndNoDefault = exhaustiveAndNoDefault(); for (int i = 0; i < numCases; ++i) { CaseTree caseTree = caseTrees.get(i); if (TreeUtils.isDefaultCaseTree(caseTree)) { @@ -2342,7 +2343,7 @@ private SwitchBuilder(Tree switchTree) { // This can be extended to handle case statements as well as case rules. boolean noFallthroughToHere = TreeUtils.isCaseRule(caseTree); boolean isLastOfExhaustive = - isLastExceptDefault && allCasesAreEnumerated() && noFallthroughToHere; + isLastExceptDefault && exhaustiveAndNoDefault && noFallthroughToHere; buildCase(caseTree, i, isLastOfExhaustive); } } @@ -2540,43 +2541,37 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastOfExhaustive) extendWithExtendedNode(new UnconditionalJump(breakTargetLC.accessLabel())); } - /** - * Returns true if the cases are exhaustive -- exactly one is executed. There might or might not - * be a `default` case label; if there is, it is never used. - * - * @return true if the cases are exhaustive - */ - private boolean allCasesAreEnumerated() { - TypeMirror selectorTypeMirror = TreeUtils.typeOf(selectorExprTree); - - switch (selectorTypeMirror.getKind()) { - case BOOLEAN: - // TODO - break; - case DECLARED: - DeclaredType declaredType = (DeclaredType) selectorTypeMirror; - TypeElement declaredTypeElement = (TypeElement) declaredType.asElement(); - if (declaredTypeElement.getKind() == ElementKind.ENUM) { - // It's an enumerated type. - List enumConstants = - ElementUtils.getEnumConstants(declaredTypeElement); - List caseLabels = new ArrayList<>(enumConstants.size()); - for (CaseTree caseTree : caseTrees) { - for (ExpressionTree caseEnumConstant : TreeUtils.caseTreeGetExpressions(caseTree)) { - if (caseEnumConstant.getKind() != Kind.IDENTIFIER) { - // This is not a simple switch on an enum, so all cases can't be enumerated. - return false; - } - caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); - } - } - // Could also check that the values match. - boolean result = enumConstants.size() == caseLabels.size(); - return result; + private boolean exhaustiveAndNoDefault() { + for (CaseTree caseTree : caseTrees) { + if (TreeUtils.isDefaultCaseTree(caseTree)) { + return false; + } + } + if (!TreeUtils.isSwitchStatement(switchTree)) { + return true; + } + int enumCaseLabels = 0; + for (CaseTree caseTree : caseTrees) { + for (Tree caseLabel : TreeUtils.caseTreeGetLabels(caseTree)) { + if (caseLabel.getKind() == Kind.NULL_LITERAL + || caseLabel.getKind().name().contentEquals("BINDING_PATTERN")) { + return true; } - break; - default: - break; + if (caseLabel.getKind() == Kind.IDENTIFIER) { + enumCaseLabels++; + } + } + } + + TypeMirror selectorTypeMirror = TreeUtils.typeOf(selectorExprTree); + if (selectorTypeMirror.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) selectorTypeMirror; + TypeElement declaredTypeElement = (TypeElement) declaredType.asElement(); + if (declaredTypeElement.getKind() == ElementKind.ENUM) { + // It's an enumerated type. + List enumConstants = ElementUtils.getEnumConstants(declaredTypeElement); + return enumConstants.size() == enumCaseLabels; + } } return false; } From 54f22445fdbbb2dad43ea51122e33455d18ab302 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 08:13:03 -0700 Subject: [PATCH 54/91] Tweak. --- .../checker/nullness/NullnessVisitor.java | 18 ++++++++++-------- .../test/CheckerFrameworkPerDirectoryTest.java | 3 +++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java b/checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java index 1a69afe4375..45039f315bd 100644 --- a/checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java @@ -419,14 +419,16 @@ public Void visitIf(IfTree tree, Void p) { public Void visitInstanceOf(InstanceOfTree tree, Void p) { // The "reference type" is the type after "instanceof". Tree refTypeTree = tree.getType(); - if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) { - List annotations = - TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree); - if (AnnotationUtils.containsSame(annotations, NULLABLE)) { - checker.reportError(tree, "instanceof.nullable"); - } - if (AnnotationUtils.containsSame(annotations, NONNULL)) { - checker.reportWarning(tree, "instanceof.nonnull.redundant"); + if (refTypeTree != null) { + if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) { + List annotations = + TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree); + 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. diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java index 6dfa2ed05fb..abcc71a05fa 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java @@ -144,6 +144,9 @@ protected CheckerFrameworkPerDirectoryTest( @Test public void run() { + if (testFiles.isEmpty()) { + return; + } boolean shouldEmitDebugInfo = TestUtilities.getShouldEmitDebugInfo(); List customizedOptions = customizeOptions(Collections.unmodifiableList(checkerOptions)); TestConfiguration config = From 4d32cffe8d943f2681d2f4af21dc0703d1e8aee1 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 09:15:21 -0700 Subject: [PATCH 55/91] Tweak --- .../cfg/builder/CFGTranslationPhaseOne.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index da7389b5488..d6e45028e27 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -618,7 +618,8 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { * @return the result of visiting the tree */ public Node visitDeconstructionPattern21(Tree deconstructionPatternTree, Void p) { - throw new RuntimeException("Not implemented"); + // TODO: implement deconstruction pattern. + throw new BugInCF("Deconstruction patterns are not yet implemented."); } /* --------------------------------------------------------- */ @@ -3816,15 +3817,10 @@ public Node visitInstanceOf(InstanceOfTree tree, Void p) { LocalVariableNode bindingNode; if (binding != null) { - if (!binding.getKind().name().contentEquals("DECONSTRUCTION_PATTERN")) { - bindingNode = (LocalVariableNode) scan(binding, p); - // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node - // instead. - refType = bindingNode.getType(); - } else { - bindingNode = null; - refType = TreeUtils.typeOf(binding); - } + bindingNode = (LocalVariableNode) scan(binding, p); + // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node + // instead. + refType = bindingNode.getType(); } else { refType = TreeUtils.typeOf(tree.getType()); bindingNode = null; From aaf78e8924dbf3552dfd7d0499eb8f81a2074aa3 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 11:17:56 -0700 Subject: [PATCH 56/91] Add a DeconstructorPatternNode. --- .../cfg/builder/CFGTranslationPhaseOne.java | 38 ++++---- .../cfg/node/AbstractNodeVisitor.java | 5 + .../cfg/node/DeconstructorPatternNode.java | 86 ++++++++++++++++ .../dataflow/cfg/node/InstanceOfNode.java | 51 ++++++++-- .../dataflow/cfg/node/NodeVisitor.java | 9 ++ .../common/basetype/BaseTypeVisitor.java | 4 +- .../ajava/TreeScannerWithDefaults.java | 19 ++-- .../framework/flow/CFAbstractTransfer.java | 4 +- .../checkerframework/javacutil/TreeUtils.java | 97 +++++++++++++++++++ 9 files changed, 275 insertions(+), 38 deletions(-) create mode 100644 dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index d6e45028e27..da07ffdf01d 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -105,6 +105,7 @@ import org.checkerframework.dataflow.cfg.node.ConditionalAndNode; import org.checkerframework.dataflow.cfg.node.ConditionalNotNode; import org.checkerframework.dataflow.cfg.node.ConditionalOrNode; +import org.checkerframework.dataflow.cfg.node.DeconstructorPatternNode; import org.checkerframework.dataflow.cfg.node.DoubleLiteralNode; import org.checkerframework.dataflow.cfg.node.EqualToNode; import org.checkerframework.dataflow.cfg.node.ExplicitThisNode; @@ -618,8 +619,15 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { * @return the result of visiting the tree */ public Node visitDeconstructionPattern21(Tree deconstructionPatternTree, Void p) { - // TODO: implement deconstruction pattern. - throw new BugInCF("Deconstruction patterns are not yet implemented."); + List nestedPatternTrees = + TreeUtils.deconstructionPatternTreeGetNestedPatterns(deconstructionPatternTree); + List nestedPatterns = new ArrayList<>(nestedPatternTrees.size()); + for (Tree pattern : nestedPatternTrees) { + nestedPatterns.add(scan(pattern, p)); + } + + return new DeconstructorPatternNode( + TreeUtils.typeOf(deconstructionPatternTree), deconstructionPatternTree, nestedPatterns); } /* --------------------------------------------------------- */ @@ -2569,7 +2577,7 @@ private boolean exhaustiveAndNoDefault() { for (CaseTree caseTree : caseTrees) { for (Tree caseLabel : TreeUtils.caseTreeGetLabels(caseTree)) { if (caseLabel.getKind() == Kind.NULL_LITERAL - || caseLabel.getKind().name().contentEquals("BINDING_PATTERN")) { + || TreeUtils.isBindingPatternTree(caseLabel)) { return true; } if (caseLabel.getKind() == Kind.IDENTIFIER) { @@ -3811,24 +3819,18 @@ public Node visitTypeParameter(TypeParameterTree tree, Void p) { @Override public Node visitInstanceOf(InstanceOfTree tree, Void p) { + InstanceOfNode instanceOfNode; Node operand = scan(tree.getExpression(), p); - Tree binding = TreeUtils.instanceOfTreeGetPattern(tree); - TypeMirror refType; - LocalVariableNode bindingNode; - - if (binding != null) { - bindingNode = (LocalVariableNode) scan(binding, p); - // Calling InstanceOfTree#getType() returns null if a pattern exists, so use the node - // instead. - refType = bindingNode.getType(); + Tree patternTree = TreeUtils.instanceOfTreeGetPattern(tree); + if (patternTree != null) { + Node pattern = scan(patternTree, p); + instanceOfNode = new InstanceOfNode(tree, operand, pattern, pattern.getType(), types); } else { - refType = TreeUtils.typeOf(tree.getType()); - bindingNode = null; + TypeMirror refType = TreeUtils.typeOf(tree.getType()); + instanceOfNode = new InstanceOfNode(tree, operand, refType, types); } - - InstanceOfNode node = new InstanceOfNode(tree, operand, bindingNode, refType, types); - extendWithNode(node); - return node; + extendWithNode(instanceOfNode); + return instanceOfNode; } @Override diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java index a424ef8b7dc..3fbbb50e034 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java @@ -384,4 +384,9 @@ public R visitMarker(MarkerNode n, P p) { public R visitExpressionStatement(ExpressionStatementNode n, P p) { return visitNode(n, p); } + + @Override + public R visitDeconstructorPattern(DeconstructorPatternNode n, P p) { + return visitNode(n, p); + } } diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java new file mode 100644 index 00000000000..b15b68f1a40 --- /dev/null +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -0,0 +1,86 @@ +package org.checkerframework.dataflow.cfg.node; + +import com.sun.source.tree.Tree; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import javax.lang.model.type.TypeMirror; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** A node for a deconstrutor pattern. */ +public class DeconstructorPatternNode extends Node { + /** The {@code DeconstructorPatternTree}. */ + private Tree deconstructorPattern; + + /** A list of nested pattern nodes. */ + private List nestedPatterns; + + /** + * Creates a {@code DeconstructorPatternNode} + * + * @param type the type of the node + * @param deconstructorPattern {@code DeconstructorPatternTree} + * @param nestedPatterns a list of nested pattern nodes + */ + public DeconstructorPatternNode( + TypeMirror type, Tree deconstructorPattern, List nestedPatterns) { + super(type); + this.deconstructorPattern = deconstructorPattern; + this.nestedPatterns = nestedPatterns; + } + + @Override + public @Nullable Tree getTree() { + return deconstructorPattern; + } + + /** + * Returns the list of nested patterns. + * + * @return the list of nested patterns + */ + public List getNestedPatterns() { + return nestedPatterns; + } + + @Override + public R accept(NodeVisitor visitor, P p) { + return visitor.visitDeconstructorPattern(this, p); + } + + @Override + public Collection getOperands() { + return nestedPatterns; + } + + /** + * A list of nested binding variables. This is lazily initialized and should only be accessed by + * {@link #getBindingVariables()}. + */ + private List bindingVariables = null; + + /** + * Return a list of all the binding variables in this pattern. + * + * @return a list of all the binding variables + */ + public List getBindingVariables() { + if (bindingVariables == null && nestedPatterns.isEmpty()) { + bindingVariables = Collections.emptyList(); + return bindingVariables; + } + if (bindingVariables == null) { + List bindingVars = new ArrayList<>(nestedPatterns.size()); + for (Node patternNode : nestedPatterns) { + if (patternNode instanceof LocalVariableNode) { + bindingVars.add((LocalVariableNode) patternNode); + } else { + bindingVars.addAll(((DeconstructorPatternNode) patternNode).getBindingVariables()); + } + } + this.bindingVariables = bindingVars; + } + return bindingVariables; + } +} diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java index bd8bc8c5051..121a73ee9fc 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java @@ -3,6 +3,7 @@ import com.sun.source.tree.InstanceOfTree; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Objects; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -27,8 +28,8 @@ public class InstanceOfNode extends Node { /** The tree associated with this node. */ protected final InstanceOfTree tree; - /** The node of the binding variable if one exists. */ - protected final @Nullable LocalVariableNode bindingVariable; + /** The node of the pattern variable if one exists. */ + protected final @Nullable Node patternNode; /** For Types.isSameType. */ protected final Types types; @@ -50,14 +51,14 @@ public InstanceOfNode(InstanceOfTree tree, Node operand, TypeMirror refType, Typ * * @param tree instanceof tree * @param operand the expression in the instanceof tree - * @param bindingVariable the binding variable or null if there is none + * @param patternNode the pattern node or null if there is none * @param refType the type in the instanceof * @param types types util */ public InstanceOfNode( InstanceOfTree tree, Node operand, - @Nullable LocalVariableNode bindingVariable, + @Nullable Node patternNode, TypeMirror refType, Types types) { super(types.getPrimitiveType(TypeKind.BOOLEAN)); @@ -65,7 +66,7 @@ public InstanceOfNode( this.operand = operand; this.refType = refType; this.types = types; - this.bindingVariable = bindingVariable; + this.patternNode = patternNode; } public Node getOperand() { @@ -76,9 +77,45 @@ public Node getOperand() { * Returns the binding variable for this instanceof, or null if one does not exist. * * @return the binding variable for this instanceof, or null if one does not exist + * @deprecated Use {@link #getPatternNode()} or {@link #getBindingVariables()} instead. */ + @Deprecated public @Nullable LocalVariableNode getBindingVariable() { - return bindingVariable; + if (patternNode instanceof LocalVariableNode) { + return (LocalVariableNode) patternNode; + } + return null; + } + + List bindingVariables = null; + + /** + * Return a list of all the binding variables in this instance of. + * + * @return a list of all the binding variables + */ + public List getBindingVariables() { + if (bindingVariables == null && patternNode == null) { + bindingVariables = Collections.emptyList(); + return bindingVariables; + } + if (bindingVariables == null) { + if (patternNode instanceof LocalVariableNode) { + bindingVariables = Collections.singletonList((LocalVariableNode) patternNode); + } else { + bindingVariables = ((DeconstructorPatternNode) patternNode).getBindingVariables(); + } + } + return bindingVariables; + } + + /** + * Returns the pattern for this instanceof, or null if one does not exist. + * + * @return the pattern for this instanceof, or null if one does not exist + */ + public @Nullable Node getPatternNode() { + return patternNode; } /** @@ -106,7 +143,7 @@ public String toString() { + getOperand() + " instanceof " + TypesUtils.simpleTypeName(getRefType()) - + (bindingVariable == null ? "" : " " + getBindingVariable()) + + (patternNode == null ? "" : " " + getPatternNode()) + ")"; } diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java index fb9cf62fe25..ad6be81daa9 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java @@ -176,4 +176,13 @@ public interface NodeVisitor { * @return the return value of the operation implemented by this visitor */ R visitExpressionStatement(ExpressionStatementNode n, P p); + + /** + * Visits a deconstructor pattern node. + * + * @param n the {@link DeconstructorPatternNode} to be visited + * @param p the argument for the operation implemented by this visitor + * @return the return value of the operation implemented by this visitor + */ + R visitDeconstructorPattern(DeconstructorPatternNode n, P p); } diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index 467de41deb6..0ab9e74bd0d 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -2577,7 +2577,7 @@ public Void visitInstanceOf(InstanceOfTree tree, Void p) { // The "reference type" is the type after "instanceof". Tree patternTree = TreeUtils.instanceOfTreeGetPattern(tree); if (patternTree != null) { - if (patternTree.getKind().name().equals("BINDING_PATTERN")) { + if (TreeUtils.isBindingPatternTree(patternTree)) { VariableTree variableTree = TreeUtils.bindingPatternTreeGetVariable(patternTree); validateTypeOf(variableTree); if (variableTree.getModifiers() != null) { @@ -2588,7 +2588,7 @@ public Void visitInstanceOf(InstanceOfTree tree, Void p) { } } } else { - // TODO: + // TODO: implement deconstructed patterns. } } else { Tree refTypeTree = tree.getType(); diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/TreeScannerWithDefaults.java b/framework/src/main/java/org/checkerframework/framework/ajava/TreeScannerWithDefaults.java index 8a72da509c7..77b1d09a629 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/TreeScannerWithDefaults.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/TreeScannerWithDefaults.java @@ -79,15 +79,16 @@ public abstract class TreeScannerWithDefaults extends TreeScanner { @Override public Void scan(Tree tree, Void unused) { if (tree != null && SystemUtil.jreVersion >= 14) { - if (tree.getKind().name().equals("SWITCH_EXPRESSION")) { - visitSwitchExpression17(tree, unused); - return null; - } else if (tree.getKind().name().equals("YIELD")) { - visitYield17(tree, unused); - return null; - } else if (tree.getKind().name().equals("BINDING_PATTERN")) { - visitBindingPattern17(tree, unused); - return null; + switch (tree.getKind().name()) { + case "SWITCH_EXPRESSION": + visitSwitchExpression17(tree, unused); + return null; + case "YIELD": + visitYield17(tree, unused); + return null; + case "BINDING_PATTERN": + visitBindingPattern17(tree, unused); + return null; } } return super.scan(tree, unused); diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index 8bd178f492a..f4105152642 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -971,8 +971,8 @@ public TransferResult visitMethodInvocation( @Override public TransferResult visitInstanceOf(InstanceOfNode node, TransferInput in) { TransferResult result = super.visitInstanceOf(node, in); - if (node.getBindingVariable() != null) { - JavaExpression expr = JavaExpression.fromNode(node.getBindingVariable()); + for (LocalVariableNode bindingVar : node.getBindingVariables()) { + JavaExpression expr = JavaExpression.fromNode(bindingVar); AnnotatedTypeMirror expType = analysis.atypeFactory.getAnnotatedType(node.getTree().getExpression()); for (AnnotationMirror anno : expType.getPrimaryAnnotations()) { diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 7ff1183177e..c6cf6b82150 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -149,6 +149,16 @@ private TreeUtils() { /** The BindingPatternTree.getVariable method for Java 16 and higher; null otherwise. */ private static final @Nullable Method BINDINGPATTERNTREE_GETVARIABLE; + /** + * The DeconstructionPatternTree.getDeconstructor method for Java 21 and higher; null otherwise. + */ + private static final @Nullable Method DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR; + + /** + * The DeconstructionPatternTree.getNestedPatterns method for Java 21 and higher; null otherwise. + */ + private static final @Nullable Method DECONSTRUCTIONPATTERNTREE_GETNESTEDPATTERNS; + /** The CaseTree.getLabels method for Java 21 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETLABELS; @@ -281,11 +291,20 @@ private TreeUtils() { TREEMAKER_SELECT = TreeMaker.class.getMethod("Select", JCExpression.class, Symbol.class); CASETREE_GETLABELS = CaseTree.class.getDeclaredMethod("getLabels"); + + Class deconstructionPatternClass = + Class.forName("com.sun.source.tree.DeconstructionPatternTree"); + DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR = + deconstructionPatternClass.getMethod("getDeconstructor"); + DECONSTRUCTIONPATTERNTREE_GETNESTEDPATTERNS = + deconstructionPatternClass.getMethod("getNestedPatterns"); } else { TREEMAKER_SELECT = null; CASETREE_GETLABELS = null; CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION = null; PATTERNCASELABELTREE_GETPATTERN = null; + DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR = null; + DECONSTRUCTIONPATTERNTREE_GETNESTEDPATTERNS = null; } } catch (ClassNotFoundException | NoSuchMethodException e) { Error err = new AssertionError("Unexpected error in TreeUtils static initializer"); @@ -2571,6 +2590,16 @@ public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { } } + /** + * Returns whether {@code tree} is a {@code BindingPatternTree}. + * + * @param tree a tree to check + * @return whether {@code tree} is a {@code BindingPatternTree} + */ + public static boolean isBindingPatternTree(Tree tree) { + return tree.getKind().name().contentEquals("BINDING_PATTERN"); + } + /** * Returns the binding variable of {@code bindingPatternTree}. * @@ -2601,6 +2630,74 @@ public static VariableTree bindingPatternTreeGetVariable(Tree bindingPatternTree } } + /** + * Returns whether {@code tree} is a {@code DeconstructionPatternTree}. + * + * @param tree a tree to check + * @return whether {@code tree} is a {@code DeconstructionPatternTree} + */ + public static boolean isDeconstructionPatternTree(Tree tree) { + return tree.getKind().name().contentEquals("DECONSTRUCTION_PATTERN"); + } + + /** + * Wrapper around {@code DeconstructionPatternTree#getDeconstructor}. + * + * @param tree the DeconstructionPatternTree + * @return the deconstructor of {@code DeconstructionPatternTree} + */ + public static ExpressionTree deconstructionPatternTreeGetDeconstructor(Tree tree) { + if (atLeastJava21) { + ExpressionTree exprTree; + try { + exprTree = (ExpressionTree) DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR.invoke(tree); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetDeconstructor: reflection failed for tree: %s", + tree, e); + } + if (exprTree != null) { + return exprTree; + } + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetDeconstructor: deconstructor is null for tree: %s", + tree); + } else { + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetDeconstructor: requires at least Java 21"); + } + } + + /** + * Wrapper around {@code DeconstructionPatternTree#getNestedPatterns}. + * + * @param tree the DeconstructionPatternTree + * @return the nested patterns of {@code DeconstructionPatternTree} + */ + @SuppressWarnings("unchecked") + public static List deconstructionPatternTreeGetNestedPatterns(Tree tree) { + if (atLeastJava21) { + List nestedPatterns; + try { + nestedPatterns = + (List) DECONSTRUCTIONPATTERNTREE_GETNESTEDPATTERNS.invoke(tree); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetNestedPatterns: reflection failed for tree: %s", + tree, e); + } + if (nestedPatterns != null) { + return nestedPatterns; + } + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetNestedPatterns: nested patterns are null for tree: %s", + tree); + } else { + throw new BugInCF( + "TreeUtils.deconstructionPatternTreeGetNestedPatterns: requires at least Java 21"); + } + } + /** * Returns the pattern of {@code instanceOfTree} tree. Returns null if the instanceof does not * have a pattern, including if the JDK version does not support instance-of patterns. From 747c02286bb98f2c2db466081677f14fdff6d984 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 12:10:49 -0700 Subject: [PATCH 57/91] Checkpoint. --- .../nullness/java17/SwitchTestIssue5412.java | 2 +- checker/tests/nullness/java21/FlowSwitch.java | 81 +++++++++++++++++++ .../cfg/builder/CFGTranslationPhaseOne.java | 7 +- .../tests/all-systems/java21/JEP440.java | 71 ++++++++++++++++ 4 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 checker/tests/nullness/java21/FlowSwitch.java create mode 100644 framework/tests/all-systems/java21/JEP440.java diff --git a/checker/tests/nullness/java17/SwitchTestIssue5412.java b/checker/tests/nullness/java17/SwitchTestIssue5412.java index e7efc937ea5..4d1bdc20cc6 100644 --- a/checker/tests/nullness/java17/SwitchTestIssue5412.java +++ b/checker/tests/nullness/java17/SwitchTestIssue5412.java @@ -30,9 +30,9 @@ public String foo1a(MyEnum b) { // The default case is dead code, so it would be possible for type-checking // to skip it and not issue this warning. But giving the warning is also // good. - // :: error: (switch.expression) default -> null; }; + // :: error: (return) return s; } diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java new file mode 100644 index 00000000000..67b270c2d02 --- /dev/null +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -0,0 +1,81 @@ +// @below-java21-jdk-skip-test +// @infer-jaifs-skip-test +// @infer-ajava-skip-test +public class FlowSwitch { + + void test1(Integer i) { + String msg = null; + switch (i) { + case -1, 1: + msg = "-1 or 1"; + break; + case Integer j when j > 0: + msg = "pos"; + break; + case Integer j: + msg = "everything else"; + break; + } + msg.toString(); + } + + void test2(Integer i) { + String msg = null; + switch (i) { + case -1, 1: + msg = "-1 or 1"; + break; + default: + msg = "everythingything else"; + break; + case 2: + msg = "pos"; + break; + } + } + + class A {} + + class B extends A {} + + sealed interface I permits C, D {} + + final class C implements I {} + + final class D implements I {} + + record Pair(T x, T y) {} + + void testE(Pair p1) { + B e = switch (p1) { + case Pair(A a, B b) -> b; + case Pair(B b, A a) -> b; + default -> null; + }; + B e2 = null; + switch (p1) { + case Pair(A a, B b) -> e2 = b; + case Pair(B b, A a) -> e2 = b; + default -> e2 = new B(); + } + e2.toString(); + } + + void test3(Pair p2) { + String s = null; + I e = null; + switch (p2) { + case Pair(I i, C c) -> {e = c; s="";} + case Pair(I i, D d) -> {e = d; s="";} + } + s.toString(); + e.toString(); + + I e2 = null; + switch (p2) { + case Pair(C c, I i) -> e2 = c; + case Pair(D d, C c) -> e2 = d; + case Pair(D d1, D d2) -> e2 = d2; + } + } +} diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index da07ffdf01d..521dcf5ef39 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2364,9 +2364,9 @@ private SwitchBuilder(Tree switchTree) { i == numCases - 1 || (i == numCases - 2 && TreeUtils.isDefaultCaseTree(caseTrees.get(i + 1))); // This can be extended to handle case statements as well as case rules. - boolean noFallthroughToHere = TreeUtils.isCaseRule(caseTree); + // boolean noFallthroughToHere = TreeUtils.isCaseRule(caseTree); boolean isLastOfExhaustive = - isLastExceptDefault && exhaustiveAndNoDefault && noFallthroughToHere; + isLastExceptDefault && exhaustiveAndNoDefault; // && noFallthroughToHere; buildCase(caseTree, i, isLastOfExhaustive); } } @@ -2577,7 +2577,8 @@ private boolean exhaustiveAndNoDefault() { for (CaseTree caseTree : caseTrees) { for (Tree caseLabel : TreeUtils.caseTreeGetLabels(caseTree)) { if (caseLabel.getKind() == Kind.NULL_LITERAL - || TreeUtils.isBindingPatternTree(caseLabel)) { + || TreeUtils.isBindingPatternTree(caseLabel) + || TreeUtils.isDeconstructionPatternTree(caseLabel)) { return true; } if (caseLabel.getKind() == Kind.IDENTIFIER) { diff --git a/framework/tests/all-systems/java21/JEP440.java b/framework/tests/all-systems/java21/JEP440.java new file mode 100644 index 00000000000..245fe134882 --- /dev/null +++ b/framework/tests/all-systems/java21/JEP440.java @@ -0,0 +1,71 @@ +// JEP 440: Record Patterns +// These are examples copied from: +// https://openjdk.org/jeps/440 + +// @below-java21-jdk-skip-test +// @infer-jaifs-skip-test +// @infer-ajava-skip-test +public class JEP440 { + + record Point(int x, int y) {} + + static void printSum(Object obj) { + if (obj instanceof Point(int x, int y)) { + System.out.println(x + y); + } + } + + enum Color {RED, GREEN, BLUE} + + record ColoredPoint(Point p, Color c) {} + + record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {} + + static void printUpperLeftColoredPoint(Rectangle r) { + if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) { + System.out.println(ul.c()); + } + } + + static void printColorOfUpperLeftPoint(Rectangle r) { + if (r instanceof Rectangle( + ColoredPoint(Point p, Color c), + ColoredPoint lr + )) { + System.out.println(c); + } + } + + static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) { + if (r instanceof Rectangle( + ColoredPoint(Point(var x, var y), var c), + var lr + )) { + System.out.println("Upper-left corner: " + x); + } + } + + void failToMatch() { + record Pair(Object x, Object y) {} + Pair p = new Pair(42, 42); + if (p instanceof Pair(String s, String t)) { + System.out.println(s + ", " + t); + } else { + System.out.println("Not a pair of strings"); + } + } + + record Box(T t) {} + + static void test1(Box> bbs) { + if (bbs instanceof Box>(Box(var s))) { + System.out.println("String " + s); + } + } + + static void test2(Box> bbs) { + if (bbs instanceof Box(Box(var s))) { + System.out.println("String " + s); + } + } +} From 985f1445ce5d3230347ae2eff86d05a7291ed539 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 12:36:02 -0700 Subject: [PATCH 58/91] More skips. --- checker/tests/nullness/java21/FlowSwitch.java | 1 + framework/tests/all-systems/java21/Issue6173.java | 1 + framework/tests/all-systems/java21/JEP440.java | 1 + framework/tests/all-systems/java21/JEP441.java | 2 ++ 4 files changed, 5 insertions(+) diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java index 67b270c2d02..76946bcd71c 100644 --- a/checker/tests/nullness/java21/FlowSwitch.java +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -1,6 +1,7 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test +// @infer-stubs-skip-test public class FlowSwitch { void test1(Integer i) { diff --git a/framework/tests/all-systems/java21/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java index 0057d079820..07feb7723dd 100644 --- a/framework/tests/all-systems/java21/Issue6173.java +++ b/framework/tests/all-systems/java21/Issue6173.java @@ -1,6 +1,7 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test +// @infer-stubs-skip-test public class Issue6173 { static Object toGroupByQueryWithExtractor2(GroupBy groupBy) { diff --git a/framework/tests/all-systems/java21/JEP440.java b/framework/tests/all-systems/java21/JEP440.java index 245fe134882..3a22382bd4e 100644 --- a/framework/tests/all-systems/java21/JEP440.java +++ b/framework/tests/all-systems/java21/JEP440.java @@ -5,6 +5,7 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test +// @infer-stubs-skip-test public class JEP440 { record Point(int x, int y) {} diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index 643ef137c3a..5bd71b3645d 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -1,6 +1,8 @@ // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test +// @infer-stubs-skip-test + // These are examples copied from: // https://openjdk.org/jeps/441 From f6d289bafe2ff6fdd9ede1d9d78623e57624b79c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 21 Sep 2023 12:37:38 -0700 Subject: [PATCH 59/91] Check for null. --- .../org/checkerframework/common/basetype/BaseTypeVisitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index 0ab9e74bd0d..384c9f6d3b2 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -1519,7 +1519,9 @@ protected void checkExplicitAnnotationsOnIntersectionBounds( public Void visitVariable(VariableTree tree, Void p) { warnAboutTypeAnnotationsTooEarly(tree, tree.getModifiers()); - visitAnnotatedType(tree.getModifiers().getAnnotations(), tree.getType()); + if (tree.getType() != null) { + visitAnnotatedType(tree.getModifiers().getAnnotations(), tree.getType()); + } AnnotatedTypeMirror variableType; if (getCurrentPath().getParentPath() != null From 8ab15ae3bfd24ff97782ca088e478dbbe23dac05 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 07:56:52 -0700 Subject: [PATCH 60/91] True positives. --- framework/tests/all-systems/java21/JEP440.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/all-systems/java21/JEP440.java b/framework/tests/all-systems/java21/JEP440.java index 3a22382bd4e..7fa9ce4474a 100644 --- a/framework/tests/all-systems/java21/JEP440.java +++ b/framework/tests/all-systems/java21/JEP440.java @@ -6,6 +6,7 @@ // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test +@SuppressWarnings("i18n") // true postives. public class JEP440 { record Point(int x, int y) {} From b6208a1714e539fc7ffa3d7632e1ce713c1c936b Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 09:26:42 -0700 Subject: [PATCH 61/91] Support guards. --- .../nullness/java21/SimpleCaseGuard.java | 28 +++++++++++++++++++ .../cfg/builder/CFGTranslationPhaseOne.java | 10 ++++++- .../dataflow/cfg/node/CaseNode.java | 19 ++++++++++++- .../framework/flow/CFAbstractTransfer.java | 6 ++++ .../checkerframework/javacutil/TreeUtils.java | 23 +++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 checker/tests/nullness/java21/SimpleCaseGuard.java diff --git a/checker/tests/nullness/java21/SimpleCaseGuard.java b/checker/tests/nullness/java21/SimpleCaseGuard.java new file mode 100644 index 00000000000..006adf0a693 --- /dev/null +++ b/checker/tests/nullness/java21/SimpleCaseGuard.java @@ -0,0 +1,28 @@ +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +// @below-java21-jdk-skip-test +// @infer-jaifs-skip-test +// @infer-ajava-skip-test +// @infer-stubs-skip-test +public class SimpleCaseGuard { + + @Nullable String field; + + void test2(Object obj, boolean b) { + switch (obj) { + case String s when field != null -> { + @NonNull String z = field; + } + case String s -> { + // :: error: (assignment) + @NonNull String z = field; + } + default -> { + // :: error: (assignment) + @NonNull String z = field; + } + } + } + +} diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 521dcf5ef39..423618f24a7 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2489,7 +2489,15 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastOfExhaustive) for (Tree exprTree : TreeUtils.caseTreeGetLabels(caseTree)) { exprs.add(scan(exprTree, null)); } - CaseNode test = new CaseNode(caseTree, selectorExprAssignment, exprs, env.getTypeUtils()); + + ExpressionTree guardTree = TreeUtils.caseTreeGetGuard(caseTree); + Node guard = null; + if (guardTree != null) { + guard = scan(guardTree, null); + } + + CaseNode test = + new CaseNode(caseTree, selectorExprAssignment, exprs, guard, env.getTypeUtils()); extendWithNode(test); extendWithExtendedNode(new ConditionalJump(thisBodyLabel, nextCaseLabel)); } diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java index 80226d453c0..c3adc492184 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java @@ -35,6 +35,9 @@ public class CaseNode extends Node { */ protected final List caseExprs; + /** The guard (the expression in the {@code when} clause) for this case. */ + protected final @Nullable Node guard; + /** * Create a new CaseNode. * @@ -45,11 +48,16 @@ public class CaseNode extends Node { * @param types a factory of utility methods for operating on types */ public CaseNode( - CaseTree tree, AssignmentNode selectorExprAssignment, List caseExprs, Types types) { + CaseTree tree, + AssignmentNode selectorExprAssignment, + List caseExprs, + @Nullable Node guard, + Types types) { super(types.getNoType(TypeKind.NONE)); this.tree = tree; this.selectorExprAssignment = selectorExprAssignment; this.caseExprs = caseExprs; + this.guard = guard; } /** @@ -72,6 +80,15 @@ public List getCaseOperands() { return caseExprs; } + /** + * Gets the node for the guard. + * + * @return the node for the guard + */ + public @Nullable Node getGuard() { + return guard; + } + @Override public CaseTree getTree() { return tree; diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index f4105152642..a0cdfb3bc6e 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -1195,6 +1195,12 @@ public TransferResult visitCase(CaseNode n, TransferInput in) { null, thenStore, elseStore, lubResult.storeChanged() || result.storeChanged()); } } + // if(n.getGuard() != null) { + // Node guard = n.getGuard(); + // TransferResult result = guard.accept(this, in); + // + // + // } return lubResult; } diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index c6cf6b82150..6888ad3cf11 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -162,6 +162,9 @@ private TreeUtils() { /** The CaseTree.getLabels method for Java 21 and higher; null otherwise. */ private static final @Nullable Method CASETREE_GETLABELS; + /** The CaseTree.getGuard method for Java 21 and higher; null otherwise. */ + private static final @Nullable Method CASETREE_GETGUARD; + /** * The ConstantCaseLabelTree.getConstantExpression method for Java 21 and higher; null otherwise. */ @@ -291,6 +294,7 @@ private TreeUtils() { TREEMAKER_SELECT = TreeMaker.class.getMethod("Select", JCExpression.class, Symbol.class); CASETREE_GETLABELS = CaseTree.class.getDeclaredMethod("getLabels"); + CASETREE_GETGUARD = CaseTree.class.getDeclaredMethod("getGuard"); Class deconstructionPatternClass = Class.forName("com.sun.source.tree.DeconstructionPatternTree"); @@ -301,6 +305,7 @@ private TreeUtils() { } else { TREEMAKER_SELECT = null; CASETREE_GETLABELS = null; + CASETREE_GETGUARD = null; CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION = null; PATTERNCASELABELTREE_GETPATTERN = null; DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR = null; @@ -2490,6 +2495,24 @@ public static List caseTreeGetLabels(CaseTree caseTree) { } } + /** + * Wrapper around {@code CaseTree#getGuard}. + * + * @param caseTree the case tree + * @return the guard on the case tree or null if one does not exist + */ + public static @Nullable ExpressionTree caseTreeGetGuard(CaseTree caseTree) { + if (atLeastJava21) { + try { + return (ExpressionTree) CASETREE_GETGUARD.invoke(caseTree); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.caseTreeGetGuard: reflection failed for tree: %s", caseTree, e); + } + } + return null; + } + /** * Returns whether {@code tree} is a {@code DefaultCaseLabelTree}. * From 8606ede133d95312e8a6834f8f2afd46fdd77b3a Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 09:46:42 -0700 Subject: [PATCH 62/91] Javadoc. --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 10 ++++++---- .../checkerframework/dataflow/cfg/node/CaseNode.java | 1 + .../dataflow/cfg/node/InstanceOfNode.java | 7 ++++++- .../test/CheckerFrameworkPerDirectoryTest.java | 1 + .../framework/flow/CFAbstractTransfer.java | 6 ------ 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 423618f24a7..fbfab499176 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2363,10 +2363,7 @@ private SwitchBuilder(Tree switchTree) { boolean isLastExceptDefault = i == numCases - 1 || (i == numCases - 2 && TreeUtils.isDefaultCaseTree(caseTrees.get(i + 1))); - // This can be extended to handle case statements as well as case rules. - // boolean noFallthroughToHere = TreeUtils.isCaseRule(caseTree); - boolean isLastOfExhaustive = - isLastExceptDefault && exhaustiveAndNoDefault; // && noFallthroughToHere; + boolean isLastOfExhaustive = isLastExceptDefault && exhaustiveAndNoDefault; buildCase(caseTree, i, isLastOfExhaustive); } } @@ -2572,6 +2569,11 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastOfExhaustive) extendWithExtendedNode(new UnconditionalJump(breakTargetLC.accessLabel())); } + /** + * Returns true if the switch is exhaustive and does not contain a default case. + * + * @return true if the switch is exhaustive and does not contain a default case + */ private boolean exhaustiveAndNoDefault() { for (CaseTree caseTree : caseTrees) { if (TreeUtils.isDefaultCaseTree(caseTree)) { diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java index c3adc492184..730055d8b3b 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java @@ -45,6 +45,7 @@ public class CaseNode extends Node { * @param selectorExprAssignment the Node for the assignment of the switch selector expression to * a synthetic local variable * @param caseExprs the case expression(s) to match the switch expression against + * @param guard the guard expression or null * @param types a factory of utility methods for operating on types */ public CaseNode( diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java index 121a73ee9fc..b9b3adf828e 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java @@ -8,6 +8,7 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.javacutil.TypesUtils; @@ -87,7 +88,11 @@ public Node getOperand() { return null; } - List bindingVariables = null; + /** + * A list of all binding variables in this instance of. This is lazy initialized, use {@link + * #getBindingVariables()}. + */ + @MonotonicNonNull List bindingVariables = null; /** * Return a list of all the binding variables in this instance of. diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java index abcc71a05fa..aec82245102 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java @@ -142,6 +142,7 @@ protected CheckerFrameworkPerDirectoryTest( // this.checkerOptions.add("-AajavaChecks"); } + /** Run the tests. */ @Test public void run() { if (testFiles.isEmpty()) { diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index a0cdfb3bc6e..f4105152642 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -1195,12 +1195,6 @@ public TransferResult visitCase(CaseNode n, TransferInput in) { null, thenStore, elseStore, lubResult.storeChanged() || result.storeChanged()); } } - // if(n.getGuard() != null) { - // Node guard = n.getGuard(); - // TransferResult result = guard.accept(this, in); - // - // - // } return lubResult; } From c581c680f93258f4d1a31ffe7fbabc928e1ae32b Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 09:49:15 -0700 Subject: [PATCH 63/91] Update change log. --- docs/CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6ec8df3d1e7..f64fffdf538 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,11 +4,16 @@ Version 3.39.0 (October 1, 2023) **User-visible changes:** The Checker Framework runs under JDK 21 -- that is, it runs on a version 21 JVM. -It does not yet support all new Java 21 language features -- it may crash when -run on a program with new Java 21 language features. +It does not yet soundly check all new Java 21 language features, but it should not crash +on them. **Implementation details:** +Dataflow supports all the new Java 21 langauge features. A new node, +`DeconstructorPatternNode`, was added, so any implementation of `NodeVisitor` will +need to be updated. + + WPI uses 1-based indexing for formal parameters and arguments. **Closed issues:** From a48946d9da17214355d9ed1c3b7ba8362a6045d1 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 10:36:16 -0700 Subject: [PATCH 64/91] Fix it. --- .../cfg/node/DeconstructorPatternNode.java | 3 ++- .../dataflow/cfg/node/InstanceOfNode.java | 10 ++++------ .../framework/source/SourceChecker.java | 16 +++++++++++----- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java index b15b68f1a40..3042e86628d 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import javax.lang.model.type.TypeMirror; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; /** A node for a deconstrutor pattern. */ @@ -58,7 +59,7 @@ public Collection getOperands() { * A list of nested binding variables. This is lazily initialized and should only be accessed by * {@link #getBindingVariables()}. */ - private List bindingVariables = null; + private @MonotonicNonNull List bindingVariables = null; /** * Return a list of all the binding variables in this pattern. diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java index b9b3adf828e..84587a99485 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java @@ -100,15 +100,13 @@ public Node getOperand() { * @return a list of all the binding variables */ public List getBindingVariables() { - if (bindingVariables == null && patternNode == null) { - bindingVariables = Collections.emptyList(); - return bindingVariables; - } if (bindingVariables == null) { - if (patternNode instanceof LocalVariableNode) { + if (patternNode instanceof DeconstructorPatternNode) { + bindingVariables = ((DeconstructorPatternNode) patternNode).getBindingVariables(); + } else if (patternNode instanceof LocalVariableNode) { bindingVariables = Collections.singletonList((LocalVariableNode) patternNode); } else { - bindingVariables = ((DeconstructorPatternNode) patternNode).getBindingVariables(); + bindingVariables = Collections.emptyList(); } } return bindingVariables; diff --git a/framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java b/framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java index a20f321603a..c865168615b 100644 --- a/framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java +++ b/framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java @@ -2573,11 +2573,17 @@ private void logTypeSystemError(TypeSystemError ce) { * @param ce the internal error to output */ private void logBugInCF(BugInCF ce) { - logBug( - ce, - "The Checker Framework crashed. Please report the crash. Version: Checker Framework " - + getCheckerVersion() - + "."); + String checkerVersion; + try { + checkerVersion = getCheckerVersion(); + } catch (Exception ex) { + checkerVersion = null; + } + String msg = "The Checker Framework crashed. Please report the crash. "; + if (checkerVersion != null) { + msg += String.format("Version: Checker Framework %s. ", checkerVersion); + } + logBug(ce, msg); } /** From 7d1e592186f2a5fff0e1261fbf6b412c46ea0c59 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 10:45:25 -0700 Subject: [PATCH 65/91] More checks for null. --- .../org/checkerframework/common/aliasing/AliasingVisitor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java index fc8f9c8497a..24a5afacae2 100644 --- a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java @@ -224,12 +224,12 @@ public Void visitVariable(VariableTree tree, Void p) { VariableElement elt = TreeUtils.elementFromDeclaration(tree); if (elt.getKind().isField() && varType.hasExplicitAnnotation(Unique.class)) { checker.reportError(tree, "unique.location.forbidden"); - } else if (tree.getType().getKind() == Tree.Kind.ARRAY_TYPE) { + } else if (tree.getType() != null && tree.getType().getKind() == Tree.Kind.ARRAY_TYPE) { AnnotatedArrayType arrayType = (AnnotatedArrayType) varType; if (arrayType.getComponentType().hasPrimaryAnnotation(Unique.class)) { checker.reportError(tree, "unique.location.forbidden"); } - } else if (tree.getType().getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + } else if (tree.getType() != null && tree.getType().getKind() == Tree.Kind.PARAMETERIZED_TYPE) { AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) varType; for (AnnotatedTypeMirror atm : declaredType.getTypeArguments()) { if (atm.hasPrimaryAnnotation(Unique.class)) { From 84f8e40e60d5407e41efbb02d7a1951e72d87b9c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 22 Sep 2023 11:34:03 -0700 Subject: [PATCH 66/91] Skip some checks. --- .../framework/test/CheckerFrameworkPerDirectoryTest.java | 3 +-- .../checkerframework/common/basetype/BaseTypeVisitor.java | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java index aec82245102..4304eb64f0a 100644 --- a/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java +++ b/framework-test/src/main/java/org/checkerframework/framework/test/CheckerFrameworkPerDirectoryTest.java @@ -138,8 +138,7 @@ protected CheckerFrameworkPerDirectoryTest( this.testDir = testDir; this.classpathExtra = classpathExtra; this.checkerOptions = new ArrayList<>(Arrays.asList(checkerOptions)); - // TODO: Reinstate, javaparser crashes on new Java 21 language features. - // this.checkerOptions.add("-AajavaChecks"); + this.checkerOptions.add("-AajavaChecks"); } /** Run the tests. */ diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index 384c9f6d3b2..b5fe6748428 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -413,7 +413,9 @@ public Void scan(@Nullable Tree tree, Void p) { *

Subclasses may override this method to disable the test if even the option is provided. */ protected void testJointJavacJavaParserVisitor() { - if (root == null || !ajavaChecks) { + if (root == null + || !ajavaChecks + || root.getSourceFile().toUri().toString().contains("java21")) { return; } @@ -459,7 +461,9 @@ public void defaultJointAction( *

Subclasses may override this method to disable the test even if the option is provided. */ protected void testAnnotationInsertion() { - if (root == null || !ajavaChecks) { + if (root == null + || !ajavaChecks + || root.getSourceFile().toUri().toString().contains("java21")) { return; } From 3f498dd3545a6fd2c981da5899ac65089cf02d39 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 23 Sep 2023 09:48:23 -0700 Subject: [PATCH 67/91] Prevent nullness errors --- .../checkerframework/javacutil/TreeUtils.java | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 6888ad3cf11..c208fdad58a 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -2460,7 +2460,8 @@ public static List caseTreeGetExpressions(CaseTree cas public static List caseTreeGetLabels(CaseTree caseTree) { try { if (atLeastJava21) { - @SuppressWarnings({"unchecked", "nullness"}) + assert CASETREE_GETLABELS != null : "@AssumeAssertion(nullness): tested atLeastJava21"; + @SuppressWarnings("unchecked") // These are caseLabelTrees. @NonNull List caseLabelTrees = (List) CASETREE_GETLABELS.invoke(caseTree); @@ -2476,7 +2477,8 @@ public static List caseTreeGetLabels(CaseTree caseTree) { } return unWrappedLabels; } else if (atLeastJava12) { - @SuppressWarnings({"unchecked", "nullness"}) + assert CASETREE_GETEXPRESSION != null : "@AssumeAssertion(nullness): tested atLeastJava12"; + @SuppressWarnings("unchecked") @NonNull List result = (List) CASETREE_GETEXPRESSIONS.invoke(caseTree); return result; @@ -2502,15 +2504,15 @@ public static List caseTreeGetLabels(CaseTree caseTree) { * @return the guard on the case tree or null if one does not exist */ public static @Nullable ExpressionTree caseTreeGetGuard(CaseTree caseTree) { - if (atLeastJava21) { - try { - return (ExpressionTree) CASETREE_GETGUARD.invoke(caseTree); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new BugInCF( - "TreeUtils.caseTreeGetGuard: reflection failed for tree: %s", caseTree, e); - } + if (!atLeastJava21) { + return null; + } + assert CASETREE_GETGUARD != null : "@AssumeAssertion(nullness): tested atLeastJava21"; + try { + return (ExpressionTree) CASETREE_GETGUARD.invoke(caseTree); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF("TreeUtils.caseTreeGetGuard: reflection failed for tree: %s", caseTree, e); } - return null; } /** @@ -2541,22 +2543,22 @@ public static boolean isConstantCaseLabelTree(Tree tree) { */ public static ExpressionTree constantCaseLabelTreeGetConstantExpression( Tree constantCaseLabelTree) { - if (atLeastJava21) { - try { - @SuppressWarnings("nullness") - @NonNull ExpressionTree ret = - (ExpressionTree) - CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION.invoke(constantCaseLabelTree); - return ret; - } catch (IllegalAccessException | InvocationTargetException e) { - throw new BugInCF( - "TreeUtils.constantCaseLabelTreeGetConstantExpression: reflection failed for tree: %s", - constantCaseLabelTree, e); - } - } else { + if (!atLeastJava21) { throw new BugInCF( "TreeUtils.constantCaseLabelTreeGetConstantExpression: requires at least Java 21"); } + assert CONSTANTCASELABELTREEGETCONSTANTEXPRESSION != null + : "@AssumeAssertion(nullness): tested atLeastJava21"; + try { + @NonNull ExpressionTree ret = + (ExpressionTree) + CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION.invoke(constantCaseLabelTree); + return ret; + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.constantCaseLabelTreeGetConstantExpression: reflection failed for tree: %s", + constantCaseLabelTree, e); + } } /** @@ -2576,19 +2578,19 @@ public static boolean isPatternCaseLabelTree(Tree tree) { * @return the {@code PatternTree} in the {@code patternCaseLabelTree} */ public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { - if (atLeastJava21) { - try { - @SuppressWarnings("nullness") - @NonNull Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); - return ret; - } catch (IllegalAccessException | InvocationTargetException e) { - throw new BugInCF( - "TreeUtils.patternCaseLabelTreeGetPattern: reflection failed for tree: %s", - patternCaseLabelTree, e); - } - } else { + if (!atLeastJava21) { throw new BugInCF("TreeUtils.patternCaseLabelTreeGetPattern: requires at least Java 21"); } + assert PATTERNCASELABELTREE_GETPATTERN != null + : "@AssumeAssertion(nullness): tested atLeastJava21"; + try { + @NonNull Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); + return ret; + } catch (IllegalAccessException | InvocationTargetException e) { + throw new BugInCF( + "TreeUtils.patternCaseLabelTreeGetPattern: reflection failed for tree: %s", + patternCaseLabelTree, e); + } } /** @@ -2713,7 +2715,8 @@ public static List deconstructionPatternTreeGetNestedPatterns(Tr return nestedPatterns; } throw new BugInCF( - "TreeUtils.deconstructionPatternTreeGetNestedPatterns: nested patterns are null for tree: %s", + "TreeUtils.deconstructionPatternTreeGetNestedPatterns: nested patterns are null for tree:" + + " %s", tree); } else { throw new BugInCF( From a2347a3f1e20a239b7acec555db5b81dfba2d911 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 23 Sep 2023 17:40:59 -0700 Subject: [PATCH 68/91] Fix variable name --- .../src/main/java/org/checkerframework/javacutil/TreeUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index c208fdad58a..203090b7e73 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -2547,7 +2547,7 @@ public static ExpressionTree constantCaseLabelTreeGetConstantExpression( throw new BugInCF( "TreeUtils.constantCaseLabelTreeGetConstantExpression: requires at least Java 21"); } - assert CONSTANTCASELABELTREEGETCONSTANTEXPRESSION != null + assert CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION != null : "@AssumeAssertion(nullness): tested atLeastJava21"; try { @NonNull ExpressionTree ret = From bc9a75551d90cfff37521c3ba0a7cb5b0fef05bf Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 24 Sep 2023 09:37:46 -0700 Subject: [PATCH 69/91] Avoid nullness errors --- .../checker/fenum/FenumAnnotatedTypeFactory.java | 6 +++++- .../checker/interning/InterningVisitor.java | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java index b9758b63186..13162b0581f 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java @@ -75,7 +75,11 @@ protected Set> createSupportedTypeQualifiers() { if (!Signatures.isBinaryName(qualName)) { throw new UserError("Malformed qualifier \"%s\" in -Aquals", qualName); } - qualSet.add(loader.loadExternalAnnotationClass(qualName)); + Class annoClass = loader.loadExternalAnnotationClass(qualName); + if (annoClass == null) { + throw new UserError("Cannot load qualifier \"%s\" in -Aquals", qualName); + } + qualSet.add(annoClass); } // load directories of qualifiers diff --git a/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java b/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java index 29741a47980..4db04d77463 100644 --- a/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java @@ -209,6 +209,7 @@ public Void visitBinary(BinaryTree tree, Void p) { public Void visitMethodInvocation(MethodInvocationTree tree, Void p) { if (isInvocationOfEquals(tree)) { AnnotatedTypeMirror receiverType = atypeFactory.getReceiverType(tree); + assert receiverType != null : "@AssumeAssertion(nullness)"; AnnotatedTypeMirror comp = atypeFactory.getAnnotatedType(tree.getArguments().get(0)); if (this.checker.getLintOption("dotequals", true) From 28c259df66c00ee86c77c4d60370bcd740558c96 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 24 Sep 2023 09:58:56 -0700 Subject: [PATCH 70/91] Renaming, documentation --- build.gradle | 9 ++++++--- .../cfg/builder/CFGTranslationPhaseOne.java | 14 +++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 4e93f701429..472d155472e 100644 --- a/build.gradle +++ b/build.gradle @@ -201,13 +201,16 @@ allprojects { doNotFormat += ['**/java17/', '**/*record*/'] } - // TODO: reinstate. googlejavaformat can't parse new language features. - // (As of version 1.17.0.) - // if (!isJava21orHigher) { + // As of 2023-09-24, google-java-format cannot parse Java 21 language features. + // See https://github.com/google/google-java-format/releases. if (true) { doNotFormat += ['**/java21/'] } + if (!isJava21orHigher) { + doNotFormat += ['**/java21/'] + } + format 'misc', { // define the files to apply `misc` to target '*.md', '*.tex', '.gitignore', 'Makefile' diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index fbfab499176..6607245a598 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -611,11 +611,11 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { } /** - * Visit a DeconstructionPatternTree + * Visit a DeconstructionPatternTree. * * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree to be * backward-compatible - * @param p parameter + * @param p an unused parameter * @return the result of visiting the tree */ public Node visitDeconstructionPattern21(Tree deconstructionPatternTree, Void p) { @@ -2360,11 +2360,11 @@ private SwitchBuilder(Tree switchTree) { // build the default case last. defaultIndex = i; } else { - boolean isLastExceptDefault = + boolean isLastCaseExceptDefault = i == numCases - 1 || (i == numCases - 2 && TreeUtils.isDefaultCaseTree(caseTrees.get(i + 1))); - boolean isLastOfExhaustive = isLastExceptDefault && exhaustiveAndNoDefault; - buildCase(caseTree, i, isLastOfExhaustive); + boolean isLastCaseOfExhaustive = isLastCaseExceptDefault && exhaustiveAndNoDefault; + buildCase(caseTree, i, isLastCaseOfExhaustive); } } @@ -2461,8 +2461,8 @@ private void buildSwitchExpressionVar() { /** * Build the CFG for the given case tree. * - * @param caseTree a case caseTree whose CFG to build - * @param index the index of the case caseTree in {@link #caseBodyLabels} + * @param caseTree a case tree whose CFG to build + * @param index the index of the case tree in {@link #caseBodyLabels} * @param isLastOfExhaustive true if this is the last case of an exhaustive switch statement, * with no fallthrough to it. In other words, no test of the labels is necessary. */ From 00e59435c05ebe2c03647f7771c6e1af737e5445 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 24 Sep 2023 16:52:15 -0700 Subject: [PATCH 71/91] Add TODO comments --- .../org/checkerframework/common/basetype/BaseTypeVisitor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index b5fe6748428..5bf6e1d0e92 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -415,6 +415,7 @@ public Void scan(@Nullable Tree tree, Void p) { protected void testJointJavacJavaParserVisitor() { if (root == null || !ajavaChecks + // TODO: Make annotation insertion work for Java 21. || root.getSourceFile().toUri().toString().contains("java21")) { return; } @@ -463,6 +464,7 @@ public void defaultJointAction( protected void testAnnotationInsertion() { if (root == null || !ajavaChecks + // TODO: Make annotation insertion work for Java 21. || root.getSourceFile().toUri().toString().contains("java21")) { return; } From 6bf7cf63203f451e35eb6f2cea621637eaebb3b4 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 25 Sep 2023 06:30:31 -0700 Subject: [PATCH 72/91] Fix. --- .../org/checkerframework/framework/flow/CFAbstractTransfer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java index f4105152642..dbd0555d667 100644 --- a/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java +++ b/framework/src/main/java/org/checkerframework/framework/flow/CFAbstractTransfer.java @@ -978,7 +978,6 @@ public TransferResult visitInstanceOf(InstanceOfNode node, TransferInput Date: Mon, 25 Sep 2023 11:16:43 -0700 Subject: [PATCH 73/91] Suppress more nullness warnings --- .../checkerframework/javacutil/TreeUtils.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 203090b7e73..210da16bb79 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -2460,8 +2460,10 @@ public static List caseTreeGetExpressions(CaseTree cas public static List caseTreeGetLabels(CaseTree caseTree) { try { if (atLeastJava21) { - assert CASETREE_GETLABELS != null : "@AssumeAssertion(nullness): tested atLeastJava21"; - @SuppressWarnings("unchecked") + @SuppressWarnings({ + "unchecked", + "nullness" // tested atLeastJava21, and method returns non-null + }) // These are caseLabelTrees. @NonNull List caseLabelTrees = (List) CASETREE_GETLABELS.invoke(caseTree); @@ -2477,8 +2479,10 @@ public static List caseTreeGetLabels(CaseTree caseTree) { } return unWrappedLabels; } else if (atLeastJava12) { - assert CASETREE_GETEXPRESSION != null : "@AssumeAssertion(nullness): tested atLeastJava12"; - @SuppressWarnings("unchecked") + @SuppressWarnings({ + "unchecked", + "nullness" // tested atLeastJava21, and method returns non-null + }) @NonNull List result = (List) CASETREE_GETEXPRESSIONS.invoke(caseTree); return result; @@ -2547,9 +2551,8 @@ public static ExpressionTree constantCaseLabelTreeGetConstantExpression( throw new BugInCF( "TreeUtils.constantCaseLabelTreeGetConstantExpression: requires at least Java 21"); } - assert CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION != null - : "@AssumeAssertion(nullness): tested atLeastJava21"; try { + @SuppressWarnings("nullness") // tested atLeastJava21, and method returns non-null @NonNull ExpressionTree ret = (ExpressionTree) CONSTANTCASELABELTREE_GETCONSTANTEXPRESSION.invoke(constantCaseLabelTree); @@ -2581,9 +2584,8 @@ public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { if (!atLeastJava21) { throw new BugInCF("TreeUtils.patternCaseLabelTreeGetPattern: requires at least Java 21"); } - assert PATTERNCASELABELTREE_GETPATTERN != null - : "@AssumeAssertion(nullness): tested atLeastJava21"; try { + @SuppressWarnings("nullness") // tested atLeastJava21, and method returns non-null @NonNull Tree ret = (Tree) PATTERNCASELABELTREE_GETPATTERN.invoke(patternCaseLabelTree); return ret; } catch (IllegalAccessException | InvocationTargetException e) { @@ -2673,6 +2675,8 @@ public static boolean isDeconstructionPatternTree(Tree tree) { */ public static ExpressionTree deconstructionPatternTreeGetDeconstructor(Tree tree) { if (atLeastJava21) { + assert DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR != null + : "@AssumeAssertion(nullness): tested atLeastJava21"; ExpressionTree exprTree; try { exprTree = (ExpressionTree) DECONSTRUCTIONPATTERNTREE_GETDECONSTRUCTOR.invoke(tree); @@ -2702,6 +2706,8 @@ public static ExpressionTree deconstructionPatternTreeGetDeconstructor(Tree tree @SuppressWarnings("unchecked") public static List deconstructionPatternTreeGetNestedPatterns(Tree tree) { if (atLeastJava21) { + assert DECONSTRUCTIONPATTERNTREE_GETNESTEDPATTERNS != null + : "@AssumeAssertion(nullness): tested atLeastJava21"; List nestedPatterns; try { nestedPatterns = From bde9c2298f4eeef7c12e7835ea05f22f23a20237 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 25 Sep 2023 11:34:11 -0700 Subject: [PATCH 74/91] Move comments --- checker/tests/nullness/java21/FlowSwitch.java | 1 + checker/tests/nullness/java21/SimpleCaseGuard.java | 7 ++++--- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 4 ++-- framework/tests/all-systems/java21/Issue6173.java | 1 + framework/tests/all-systems/java21/JEP440.java | 9 +++++---- framework/tests/all-systems/java21/JEP441.java | 10 +++++----- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java index 76946bcd71c..52e650eb285 100644 --- a/checker/tests/nullness/java21/FlowSwitch.java +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -2,6 +2,7 @@ // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test + public class FlowSwitch { void test1(Integer i) { diff --git a/checker/tests/nullness/java21/SimpleCaseGuard.java b/checker/tests/nullness/java21/SimpleCaseGuard.java index 006adf0a693..3d77a0faf3b 100644 --- a/checker/tests/nullness/java21/SimpleCaseGuard.java +++ b/checker/tests/nullness/java21/SimpleCaseGuard.java @@ -1,10 +1,11 @@ -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + public class SimpleCaseGuard { @Nullable String field; diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index e408e8ab584..f4d37903d24 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -613,8 +613,8 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { /** * Visit a DeconstructionPatternTree. * - * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree to be - * backward-compatible + * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree so the Checker + * Framework compiles under JDK < 21 * @param p an unused parameter * @return the result of visiting the tree */ diff --git a/framework/tests/all-systems/java21/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java index 07feb7723dd..4879fcdbbf1 100644 --- a/framework/tests/all-systems/java21/Issue6173.java +++ b/framework/tests/all-systems/java21/Issue6173.java @@ -2,6 +2,7 @@ // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test + public class Issue6173 { static Object toGroupByQueryWithExtractor2(GroupBy groupBy) { diff --git a/framework/tests/all-systems/java21/JEP440.java b/framework/tests/all-systems/java21/JEP440.java index 7fa9ce4474a..02c37cdb0c6 100644 --- a/framework/tests/all-systems/java21/JEP440.java +++ b/framework/tests/all-systems/java21/JEP440.java @@ -1,11 +1,12 @@ -// JEP 440: Record Patterns -// These are examples copied from: -// https://openjdk.org/jeps/440 - // @below-java21-jdk-skip-test // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test + +// JEP 440: Record Patterns +// These are examples copied from: +// https://openjdk.org/jeps/440 + @SuppressWarnings("i18n") // true postives. public class JEP440 { diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index 5bd71b3645d..998eb0bd00d 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -9,7 +9,7 @@ @SuppressWarnings("i18n") // true postives. public class JEP441 { - // We enhance switch statements and expressions in four ways: + // JEP 441 enhances switch statements and expressions in four ways: // * Improve enum constant case labels // * Extend case labels to include patterns and null in addition to constants // * Broaden the range of types permitted for the selector expressions of both switch statements @@ -34,10 +34,10 @@ static String formatter(Object obj) { static void formatterPatternSwitchStatement(Object obj) { switch (obj) { case Integer i: String.format("int %d", i); break; - case Long l :String.format("long %d", l);break; - case Double d :String.format("double %f", d);break; - case String s :String.format("String %s", s);break; - default : obj.toString(); + case Long l : String.format("long %d", l); break; + case Double d : String.format("double %f", d); break; + case String s : String.format("String %s", s); break; + default : obj.toString(); }; } From 5a5c4e90dff32d555edd9fc22e3ddeee36c6b0c6 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 25 Sep 2023 11:41:58 -0700 Subject: [PATCH 75/91] Brevity --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index f4d37903d24..5ffa7e893e4 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2488,10 +2488,7 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastCaseOfExhaust } ExpressionTree guardTree = TreeUtils.caseTreeGetGuard(caseTree); - Node guard = null; - if (guardTree != null) { - guard = scan(guardTree, null); - } + Node guard = (guardTree == null) ? null : scan(guardTree, null); CaseNode test = new CaseNode(caseTree, selectorExprAssignment, exprs, guard, env.getTypeUtils()); From 2c8bd83f1467f2cc570ae02a9105692771bea815 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 25 Sep 2023 12:43:25 -0700 Subject: [PATCH 76/91] Add documentation --- .../cfg/builder/CFGTranslationPhaseOne.java | 11 +++++++++- .../dataflow/cfg/node/CaseNode.java | 2 +- .../cfg/node/DeconstructorPatternNode.java | 8 ++++++-- docs/CHANGELOG.md | 10 ++++++---- .../common/aliasing/AliasingVisitor.java | 20 ++++++++++--------- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 5ffa7e893e4..d1906a5efa3 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2577,12 +2577,20 @@ private boolean exhaustiveAndNoDefault() { return false; } } + + // There is no default case. Check whether the switch is exhaustive. + + // Switch expressions are always exhaustive, but they might have a default case, which is why + // the above loop is not fused with the below loop. if (!TreeUtils.isSwitchStatement(switchTree)) { return true; } + int enumCaseLabels = 0; for (CaseTree caseTree : caseTrees) { for (Tree caseLabel : TreeUtils.caseTreeGetLabels(caseTree)) { + // Java guarantees that if one of the cases is the null literal, the switch is exhaustive. + // Also if certain other constructs exist. if (caseLabel.getKind() == Kind.NULL_LITERAL || TreeUtils.isBindingPatternTree(caseLabel) || TreeUtils.isDeconstructionPatternTree(caseLabel)) { @@ -2599,11 +2607,12 @@ private boolean exhaustiveAndNoDefault() { DeclaredType declaredType = (DeclaredType) selectorTypeMirror; TypeElement declaredTypeElement = (TypeElement) declaredType.asElement(); if (declaredTypeElement.getKind() == ElementKind.ENUM) { - // It's an enumerated type. + // The switch expression's type is an enumerated type. List enumConstants = ElementUtils.getEnumConstants(declaredTypeElement); return enumConstants.size() == enumCaseLabels; } } + return false; } } diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java index 730055d8b3b..e3027fa44b9 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java @@ -82,7 +82,7 @@ public List getCaseOperands() { } /** - * Gets the node for the guard. + * Gets the node for the guard (the expression in the {@code when} clause). * * @return the node for the guard */ diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java index 640bd7a3d95..2e616fccf18 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -15,10 +15,10 @@ public class DeconstructorPatternNode extends Node { * The {@code DeconstructorPatternTree}, declared as {@link Tree} to permit this file to compile * under JDK < 21. */ - private Tree deconstructorPattern; + private final Tree deconstructorPattern; /** A list of nested pattern nodes. */ - private List nestedPatterns; + private final List nestedPatterns; /** * Creates a {@code DeconstructorPatternNode}. @@ -35,6 +35,7 @@ public DeconstructorPatternNode( } @Override + @Pure public @Nullable Tree getTree() { return deconstructorPattern; } @@ -44,6 +45,7 @@ public DeconstructorPatternNode( * * @return the nested patterns */ + @Pure public List getNestedPatterns() { return nestedPatterns; } @@ -54,6 +56,7 @@ public R accept(NodeVisitor visitor, P p) { } @Override + @Pure public Collection getOperands() { return nestedPatterns; } @@ -82,6 +85,7 @@ public List getBindingVariables() { bindingVariables.addAll(((DeconstructorPatternNode) patternNode).getBindingVariables()); } } + bindingVariables = Collections.unmodifiableList(bindingVariables); } } return bindingVariables; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index de586c46110..6fbc9aa9822 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,15 +3,17 @@ Version 3.39.0 (October 1, 2023) **User-visible changes:** -The Checker Framework runs under JDK 21 -- that is, it runs on a version 21 JVM. +The Checker Framework runs on a version 21 JVM. It does not yet soundly check all new Java 21 language features, but it does not crash when compiling them. **Implementation details:** -Dataflow supports all the new Java 21 langauge features. A new node, -`DeconstructorPatternNode`, was added, so any implementation of `NodeVisitor` will -need to be updated. +Dataflow supports all the new Java 21 langauge features. + * A new node,`DeconstructorPatternNode`, was added, so any implementation of + `NodeVisitor` must be updated. + * Method `InstanceOfNode.getBindingVariable()` is deprecated; use + `getPatternNode()` or `getBindingVariables()` instead. WPI uses 1-based indexing for formal parameters and arguments. diff --git a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java index 24a5afacae2..b7c58f53a11 100644 --- a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java @@ -224,17 +224,19 @@ public Void visitVariable(VariableTree tree, Void p) { VariableElement elt = TreeUtils.elementFromDeclaration(tree); if (elt.getKind().isField() && varType.hasExplicitAnnotation(Unique.class)) { checker.reportError(tree, "unique.location.forbidden"); - } else if (tree.getType() != null && tree.getType().getKind() == Tree.Kind.ARRAY_TYPE) { - AnnotatedArrayType arrayType = (AnnotatedArrayType) varType; - if (arrayType.getComponentType().hasPrimaryAnnotation(Unique.class)) { - checker.reportError(tree, "unique.location.forbidden"); - } - } else if (tree.getType() != null && tree.getType().getKind() == Tree.Kind.PARAMETERIZED_TYPE) { - AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) varType; - for (AnnotatedTypeMirror atm : declaredType.getTypeArguments()) { - if (atm.hasPrimaryAnnotation(Unique.class)) { + } else if (tree.getType() != null) { + if (tree.getType().getKind() == Tree.Kind.ARRAY_TYPE) { + AnnotatedArrayType arrayType = (AnnotatedArrayType) varType; + if (arrayType.getComponentType().hasPrimaryAnnotation(Unique.class)) { checker.reportError(tree, "unique.location.forbidden"); } + } else if (tree.getType().getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) varType; + for (AnnotatedTypeMirror atm : declaredType.getTypeArguments()) { + if (atm.hasPrimaryAnnotation(Unique.class)) { + checker.reportError(tree, "unique.location.forbidden"); + } + } } } return super.visitVariable(tree, p); From 51e1ef696aadc78a19e5c4cfae1008c9939d52b4 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Mon, 25 Sep 2023 14:43:58 -0700 Subject: [PATCH 77/91] Add import statement --- .../dataflow/cfg/node/DeconstructorPatternNode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java index 2e616fccf18..9731d6b449f 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -8,6 +8,7 @@ import javax.lang.model.type.TypeMirror; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; /** A node for a deconstrutor pattern. */ public class DeconstructorPatternNode extends Node { From 008400890f8fcc6c39e9bc4009c3cd0e4c51cf68 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 26 Sep 2023 08:19:54 -0700 Subject: [PATCH 78/91] Address most of the code review. --- checker/tests/nullness/java21/FlowSwitch.java | 2 ++ checker/tests/nullness/java21/SimpleCaseGuard.java | 2 ++ .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 5 +---- .../dataflow/cfg/node/InstanceOfNode.java | 2 +- framework/tests/all-systems/java21/Issue6173.java | 2 ++ framework/tests/all-systems/java21/JEP440.java | 2 ++ framework/tests/all-systems/java21/JEP441.java | 2 ++ .../org/checkerframework/javacutil/TreeUtils.java | 12 ++++++------ 8 files changed, 18 insertions(+), 11 deletions(-) diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java index 52e650eb285..4d9d091a6a1 100644 --- a/checker/tests/nullness/java21/FlowSwitch.java +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -1,4 +1,6 @@ // @below-java21-jdk-skip-test + +// None of the WPI formats support the new Java 21 languages features, so skip inference until they do. // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test diff --git a/checker/tests/nullness/java21/SimpleCaseGuard.java b/checker/tests/nullness/java21/SimpleCaseGuard.java index 3d77a0faf3b..805c93d5b91 100644 --- a/checker/tests/nullness/java21/SimpleCaseGuard.java +++ b/checker/tests/nullness/java21/SimpleCaseGuard.java @@ -1,4 +1,6 @@ // @below-java21-jdk-skip-test + +// None of the WPI formats support the new Java 21 languages features, so skip inference until they do. // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index d1906a5efa3..7bbcfccd264 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2360,10 +2360,7 @@ private SwitchBuilder(Tree switchTree) { // build the default case last. defaultIndex = i; } else { - boolean isLastCaseExceptDefault = - i == numCases - 1 - || (i == numCases - 2 && TreeUtils.isDefaultCaseTree(caseTrees.get(i + 1))); - boolean isLastCaseOfExhaustive = isLastCaseExceptDefault && exhaustiveAndNoDefault; + boolean isLastCaseOfExhaustive = (i == numCases - 1) && exhaustiveAndNoDefault; buildCase(caseTree, i, isLastCaseOfExhaustive); } } diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java index ece7a86d029..5a17045323b 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java @@ -29,7 +29,7 @@ public class InstanceOfNode extends Node { /** The tree associated with this node. */ protected final InstanceOfTree tree; - /** The node of the pattern variable if one exists. */ + /** The node of the pattern if one exists. */ protected final @Nullable Node patternNode; /** For Types.isSameType. */ diff --git a/framework/tests/all-systems/java21/Issue6173.java b/framework/tests/all-systems/java21/Issue6173.java index 4879fcdbbf1..bf48e909f18 100644 --- a/framework/tests/all-systems/java21/Issue6173.java +++ b/framework/tests/all-systems/java21/Issue6173.java @@ -1,4 +1,6 @@ // @below-java21-jdk-skip-test + +// None of the WPI formats support the new Java 21 languages features, so skip inference until they do. // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test diff --git a/framework/tests/all-systems/java21/JEP440.java b/framework/tests/all-systems/java21/JEP440.java index 02c37cdb0c6..6af52a81c04 100644 --- a/framework/tests/all-systems/java21/JEP440.java +++ b/framework/tests/all-systems/java21/JEP440.java @@ -1,4 +1,6 @@ // @below-java21-jdk-skip-test + +// None of the WPI formats support the new Java 21 languages features, so skip inference until they do. // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index 998eb0bd00d..43de14c4572 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -1,4 +1,6 @@ // @below-java21-jdk-skip-test + +// None of the WPI formats support the new Java 21 languages features, so skip inference until they do. // @infer-jaifs-skip-test // @infer-ajava-skip-test // @infer-stubs-skip-test diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 210da16bb79..a3c1b181262 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -2450,12 +2450,12 @@ public static List caseTreeGetExpressions(CaseTree cas } /** - * Get the list of expressions from a case expression. For the default case, this is empty. - * Otherwise, in JDK 11 and earlier, this is a singleton list. In JDK 12 onwards, there can be - * multiple expressions per case. + * Get the list of labels from a case expression. For {@code default}, this is empty. Otherwise, + * in JDK 11 and earlier, this is a singleton list of expression trees. In JDK 12, this is a list + * of expression trees. In JDK 21+, this is a list of expression and pattern trees. * - * @param caseTree the case expression to get the expressions from - * @return the list of {@code CaseLabelTree}s in the case + * @param caseTree the case expression to get the labels from + * @return the list of case labels in the case */ public static List caseTreeGetLabels(CaseTree caseTree) { try { @@ -2464,7 +2464,7 @@ public static List caseTreeGetLabels(CaseTree caseTree) { "unchecked", "nullness" // tested atLeastJava21, and method returns non-null }) - // These are caseLabelTrees. + // These are CaseLabelTrees. @NonNull List caseLabelTrees = (List) CASETREE_GETLABELS.invoke(caseTree); List unWrappedLabels = new ArrayList<>(); From defc2ac66a545a6d19298282cf0df3cc715edd5e Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 26 Sep 2023 08:59:28 -0700 Subject: [PATCH 79/91] Add comment. --- .../org/checkerframework/common/aliasing/AliasingVisitor.java | 1 + .../org/checkerframework/common/basetype/BaseTypeVisitor.java | 1 + .../checkerframework/framework/type/TypeFromMemberVisitor.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java index b7c58f53a11..db1a965efb2 100644 --- a/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/aliasing/AliasingVisitor.java @@ -225,6 +225,7 @@ public Void visitVariable(VariableTree tree, Void p) { if (elt.getKind().isField() && varType.hasExplicitAnnotation(Unique.class)) { checker.reportError(tree, "unique.location.forbidden"); } else if (tree.getType() != null) { + // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. if (tree.getType().getKind() == Tree.Kind.ARRAY_TYPE) { AnnotatedArrayType arrayType = (AnnotatedArrayType) varType; if (arrayType.getComponentType().hasPrimaryAnnotation(Unique.class)) { diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index 5bf6e1d0e92..65276694723 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -1526,6 +1526,7 @@ public Void visitVariable(VariableTree tree, Void p) { warnAboutTypeAnnotationsTooEarly(tree, tree.getModifiers()); if (tree.getType() != null) { + // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. visitAnnotatedType(tree.getModifiers().getAnnotations(), tree.getType()); } diff --git a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java index 7bd70054332..73d204a7635 100644 --- a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java @@ -44,7 +44,7 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp // Let normal defaulting happen for the primary annotation. result.clearPrimaryAnnotations(); } else if (variableTree.getType() == null) { - // Happens with binding variables from DeconstructionPatternTree. + // VariableTree#getType return null for binding variables from a DeconstructionPatternTree. result = f.type(variableTree); } else { // (variableTree.getType() does not include the annotation before the type, so those From 8d0b32e6d65393d3670112548c0e320ba2fe11b2 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 08:10:45 -0700 Subject: [PATCH 80/91] Add a passing test case. --- checker/tests/nullness/Issue5967.java | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 checker/tests/nullness/Issue5967.java diff --git a/checker/tests/nullness/Issue5967.java b/checker/tests/nullness/Issue5967.java new file mode 100644 index 00000000000..16f05811ae0 --- /dev/null +++ b/checker/tests/nullness/Issue5967.java @@ -0,0 +1,23 @@ +import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.NonNull; + +// @below-java17-jdk-skip-test +public final class Issue5967 { + + enum TestEnum { + FIRST, + SECOND; + } + + public static void main(String[] args) { + TestEnum testEnum = TestEnum.FIRST; + Supplier supplier = + switch (testEnum) { + case FIRST: + yield () -> 1; + case SECOND: + yield () -> 2; + }; + @NonNull Supplier supplier1 = supplier; + } +} From bdde44318a75d325f69c1a6c909d400a785e8002 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 10:24:49 -0700 Subject: [PATCH 81/91] Move test file. --- checker/tests/nullness/{ => java17}/Issue5967.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename checker/tests/nullness/{ => java17}/Issue5967.java (100%) diff --git a/checker/tests/nullness/Issue5967.java b/checker/tests/nullness/java17/Issue5967.java similarity index 100% rename from checker/tests/nullness/Issue5967.java rename to checker/tests/nullness/java17/Issue5967.java From e3efbb04797f45ac43587d3167b948bee1f8115f Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 10:26:33 -0700 Subject: [PATCH 82/91] Revert "Avoid nullness errors" This reverts commit bc9a75551d90cfff37521c3ba0a7cb5b0fef05bf. --- .../checker/fenum/FenumAnnotatedTypeFactory.java | 6 +----- .../checker/interning/InterningVisitor.java | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java index 13162b0581f..b9758b63186 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java @@ -75,11 +75,7 @@ protected Set> createSupportedTypeQualifiers() { if (!Signatures.isBinaryName(qualName)) { throw new UserError("Malformed qualifier \"%s\" in -Aquals", qualName); } - Class annoClass = loader.loadExternalAnnotationClass(qualName); - if (annoClass == null) { - throw new UserError("Cannot load qualifier \"%s\" in -Aquals", qualName); - } - qualSet.add(annoClass); + qualSet.add(loader.loadExternalAnnotationClass(qualName)); } // load directories of qualifiers diff --git a/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java b/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java index 4db04d77463..29741a47980 100644 --- a/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java @@ -209,7 +209,6 @@ public Void visitBinary(BinaryTree tree, Void p) { public Void visitMethodInvocation(MethodInvocationTree tree, Void p) { if (isInvocationOfEquals(tree)) { AnnotatedTypeMirror receiverType = atypeFactory.getReceiverType(tree); - assert receiverType != null : "@AssumeAssertion(nullness)"; AnnotatedTypeMirror comp = atypeFactory.getAnnotatedType(tree.getArguments().get(0)); if (this.checker.getLintOption("dotequals", true) From 285bd8c55975c53ca904bece3b3c665cc92a8e9d Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 27 Sep 2023 10:44:15 -0700 Subject: [PATCH 83/91] Code review tweaks --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 7bbcfccd264..686e0a7633e 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -614,7 +614,7 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { * Visit a DeconstructionPatternTree. * * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree so the Checker - * Framework compiles under JDK < 21 + * Framework compiles under JDK 20 and earlier * @param p an unused parameter * @return the result of visiting the tree */ @@ -2351,7 +2351,6 @@ private SwitchBuilder(Tree switchTree) { // Build CFG for the cases. int defaultIndex = -1; - boolean exhaustiveAndNoDefault = exhaustiveAndNoDefault(); for (int i = 0; i < numCases; ++i) { CaseTree caseTree = caseTrees.get(i); if (TreeUtils.isDefaultCaseTree(caseTree)) { @@ -2360,7 +2359,9 @@ private SwitchBuilder(Tree switchTree) { // build the default case last. defaultIndex = i; } else { - boolean isLastCaseOfExhaustive = (i == numCases - 1) && exhaustiveAndNoDefault; + boolean isLastCase = (i == numCases - 1); + boolean isLastCaseAndNoDefault = isLastCase && defaultIndex == -1; + boolean isLastCaseOfExhaustive = isLastCaseAndNoDefault && exhaustiveAndNoDefault(); buildCase(caseTree, i, isLastCaseOfExhaustive); } } From 0ca68dd2c5629023261be13b574a9ece1e61855c Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 27 Sep 2023 11:09:12 -0700 Subject: [PATCH 84/91] Code review tweaks --- .../cfg/node/DeconstructorPatternNode.java | 2 +- .../common/basetype/BaseTypeVisitor.java | 2 +- .../framework/type/TypeFromMemberVisitor.java | 3 +- .../checkerframework/javacutil/TreeUtils.java | 36 +++++++++---------- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java index 9731d6b449f..840753dc35a 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -14,7 +14,7 @@ public class DeconstructorPatternNode extends Node { /** * The {@code DeconstructorPatternTree}, declared as {@link Tree} to permit this file to compile - * under JDK < 21. + * under JDK 20 and earlier. */ private final Tree deconstructorPattern; diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index 65276694723..4bf77a55383 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -1525,8 +1525,8 @@ protected void checkExplicitAnnotationsOnIntersectionBounds( public Void visitVariable(VariableTree tree, Void p) { warnAboutTypeAnnotationsTooEarly(tree, tree.getModifiers()); + // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. if (tree.getType() != null) { - // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. visitAnnotatedType(tree.getModifiers().getAnnotations(), tree.getType()); } diff --git a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java index 73d204a7635..f5d44fa2b5d 100644 --- a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java @@ -44,7 +44,7 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp // Let normal defaulting happen for the primary annotation. result.clearPrimaryAnnotations(); } else if (variableTree.getType() == null) { - // VariableTree#getType return null for binding variables from a DeconstructionPatternTree. + // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. result = f.type(variableTree); } else { // (variableTree.getType() does not include the annotation before the type, so those @@ -77,6 +77,7 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp AnnotatedDeclaredType annotatedDeclaredType = (AnnotatedDeclaredType) result; // The underlying type of result does not have all annotations, but the TypeMirror of // variableTree.getType() does. + // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. if (variableTree.getType() != null) { DeclaredType declaredType = (DeclaredType) TreeUtils.typeOf(variableTree.getType()); AnnotatedTypes.applyAnnotationsFromDeclaredType(annotatedDeclaredType, declaredType); diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index a3c1b181262..445c9ec46ed 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -1835,7 +1835,7 @@ public static boolean isTypeDeclaration(Tree tree) { } /** - * Returns whether or not tree is an access of array length. + * Returns true if tree is an access of array length. * * @param tree tree to check * @return true if tree is an access of array length @@ -2435,7 +2435,7 @@ public static List caseTreeGetExpressions(CaseTree cas (List) CASETREE_GETEXPRESSIONS.invoke(caseTree); return result; } else { - @SuppressWarnings("nullness") + @SuppressWarnings("nullness") // tested atLeastJava12, and method returns null ExpressionTree expression = (ExpressionTree) CASETREE_GETEXPRESSION.invoke(caseTree); if (expression == null) { return Collections.emptyList(); @@ -2467,27 +2467,27 @@ public static List caseTreeGetLabels(CaseTree caseTree) { // These are CaseLabelTrees. @NonNull List caseLabelTrees = (List) CASETREE_GETLABELS.invoke(caseTree); - List unWrappedLabels = new ArrayList<>(); + List labels = new ArrayList<>(); for (Tree caseLabel : caseLabelTrees) { if (isDefaultCaseLabelTree(caseLabel)) { return Collections.emptyList(); } else if (isConstantCaseLabelTree(caseLabel)) { - unWrappedLabels.add(constantCaseLabelTreeGetConstantExpression(caseLabel)); + labels.add(constantCaseLabelTreeGetConstantExpression(caseLabel)); } else if (isPatternCaseLabelTree(caseLabel)) { - unWrappedLabels.add(patternCaseLabelTreeGetPattern(caseLabel)); + labels.add(patternCaseLabelTreeGetPattern(caseLabel)); } } - return unWrappedLabels; + return labels; } else if (atLeastJava12) { @SuppressWarnings({ "unchecked", - "nullness" // tested atLeastJava21, and method returns non-null + "nullness" // tested atLeastJava12, and method returns non-null }) @NonNull List result = (List) CASETREE_GETEXPRESSIONS.invoke(caseTree); return result; } else { - @SuppressWarnings("nullness") + @SuppressWarnings("nullness") // tested atLeastJava12, and method returns null ExpressionTree expression = (ExpressionTree) CASETREE_GETEXPRESSION.invoke(caseTree); if (expression == null) { return Collections.emptyList(); @@ -2520,20 +2520,20 @@ public static List caseTreeGetLabels(CaseTree caseTree) { } /** - * Returns whether {@code tree} is a {@code DefaultCaseLabelTree}. + * Returns true if {@code tree} is a {@code DefaultCaseLabelTree}. * * @param tree a tree to check - * @return whether {@code tree} is a {@code DefaultCaseLabelTree} + * @return true if {@code tree} is a {@code DefaultCaseLabelTree} */ public static boolean isDefaultCaseLabelTree(Tree tree) { return tree.getKind().name().contentEquals("DEFAULT_CASE_LABEL"); } /** - * Returns whether {@code tree} is a {@code ConstantCaseLabelTree}. + * Returns true if {@code tree} is a {@code ConstantCaseLabelTree}. * * @param tree a tree to check - * @return whether {@code tree} is a {@code ConstantCaseLabelTree} + * @return true if {@code tree} is a {@code ConstantCaseLabelTree} */ public static boolean isConstantCaseLabelTree(Tree tree) { return tree.getKind().name().contentEquals("CONSTANT_CASE_LABEL"); @@ -2565,10 +2565,10 @@ public static ExpressionTree constantCaseLabelTreeGetConstantExpression( } /** - * Returns whether {@code tree} is a {@code PatternCaseLabelTree}. + * Returns true if {@code tree} is a {@code PatternCaseLabelTree}. * * @param tree a tree to check - * @return whether {@code tree} is a {@code PatternCaseLabelTree} + * @return true if {@code tree} is a {@code PatternCaseLabelTree} */ public static boolean isPatternCaseLabelTree(Tree tree) { return tree.getKind().name().contentEquals("PATTERN_CASE_LABEL"); @@ -2618,10 +2618,10 @@ public static Tree patternCaseLabelTreeGetPattern(Tree patternCaseLabelTree) { } /** - * Returns whether {@code tree} is a {@code BindingPatternTree}. + * Returns true if {@code tree} is a {@code BindingPatternTree}. * * @param tree a tree to check - * @return whether {@code tree} is a {@code BindingPatternTree} + * @return true if {@code tree} is a {@code BindingPatternTree} */ public static boolean isBindingPatternTree(Tree tree) { return tree.getKind().name().contentEquals("BINDING_PATTERN"); @@ -2658,10 +2658,10 @@ public static VariableTree bindingPatternTreeGetVariable(Tree bindingPatternTree } /** - * Returns whether {@code tree} is a {@code DeconstructionPatternTree}. + * Returns true if {@code tree} is a {@code DeconstructionPatternTree}. * * @param tree a tree to check - * @return whether {@code tree} is a {@code DeconstructionPatternTree} + * @return true if {@code tree} is a {@code DeconstructionPatternTree} */ public static boolean isDeconstructionPatternTree(Tree tree) { return tree.getKind().name().contentEquals("DECONSTRUCTION_PATTERN"); From 0138c87b1df6fc636908517a70dd7850996c6405 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 11:17:20 -0700 Subject: [PATCH 85/91] Rename. --- .../javacutil/trees/TreeUtilsAfterJava11.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java index a5f1d7eeaf1..73d5f70333b 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java @@ -131,17 +131,17 @@ public static List getLabels(CaseTree caseTree) { @SuppressWarnings("unchecked") List caseLabelTrees = (List) invokeNonNullResult(GET_LABELS, caseTree); - List unWrappedLabels = new ArrayList<>(); + List labels = new ArrayList<>(); for (Tree caseLabel : caseLabelTrees) { if (TreeUtils.isDefaultCaseLabelTree(caseLabel)) { return Collections.emptyList(); } else if (TreeUtils.isConstantCaseLabelTree(caseLabel)) { - unWrappedLabels.add(ConstantCaseLabelUtils.getConstantExpression(caseLabel)); + labels.add(ConstantCaseLabelUtils.getConstantExpression(caseLabel)); } else if (TreeUtils.isPatternCaseLabelTree(caseLabel)) { - unWrappedLabels.add(PatternCaseLabelUtils.getPattern(caseLabel)); + labels.add(PatternCaseLabelUtils.getPattern(caseLabel)); } } - return unWrappedLabels; + return labels; } return getExpressions(caseTree); } From b1830009c2cc7be8b7490b3ffb3d40d4ef5fdcb4 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 12:00:33 -0700 Subject: [PATCH 86/91] Code review. --- .../checker/fenum/FenumVisitor.java | 2 +- checker/tests/nullness/java21/FlowSwitch.java | 13 ++++++ .../cfg/builder/CFGTranslationPhaseOne.java | 12 +++--- .../common/basetype/BaseTypeVisitor.java | 4 +- .../framework/ajava/ExpectedTreesVisitor.java | 4 +- .../ajava/JointJavacJavaParserVisitor.java | 10 ++--- .../javacutil/SwitchExpressionScanner.java | 6 +-- .../checkerframework/javacutil/TreeUtils.java | 15 +++---- .../{trees => }/TreeUtilsAfterJava11.java | 41 +++++++++++++++---- 9 files changed, 73 insertions(+), 34 deletions(-) rename javacutil/src/main/java/org/checkerframework/javacutil/{trees => }/TreeUtilsAfterJava11.java (93%) diff --git a/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java index 96f729f322a..0a7580c77f3 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java @@ -15,7 +15,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.javacutil.AnnotationMirrorSet; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; /** The visitor for Fenum Checker. */ public class FenumVisitor extends BaseTypeVisitor { diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java index 4d9d091a6a1..e103357346f 100644 --- a/checker/tests/nullness/java21/FlowSwitch.java +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -7,6 +7,18 @@ public class FlowSwitch { + void test0(Number n) { + String s = null; + switch (n) { + case null, default: { + // TODO: this should issue a dereference of nullable error. + n.toString(); + s = ""; + } + } + s.toString(); + } + void test1(Integer i) { String msg = null; switch (i) { @@ -36,6 +48,7 @@ void test2(Integer i) { msg = "pos"; break; } + msg.toString(); } class A {} diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 5b90e2ce6c1..fe0beceb586 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -168,16 +168,16 @@ import org.checkerframework.javacutil.SystemUtil; import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.DeconstructionPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; import org.checkerframework.javacutil.TypeAnnotationUtils; import org.checkerframework.javacutil.TypeKindUtils; import org.checkerframework.javacutil.TypesUtils; import org.checkerframework.javacutil.trees.TreeBuilder; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.DeconstructionPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; import org.plumelib.util.ArrayMap; import org.plumelib.util.ArraySet; import org.plumelib.util.CollectionsPlume; diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index fc060c02be7..6f900e062d1 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -141,9 +141,9 @@ import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TreeUtils.MemberReferenceKind; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; import org.checkerframework.javacutil.TypesUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; import org.plumelib.util.ArrayMap; import org.plumelib.util.ArraySet; import org.plumelib.util.ArraysPlume; diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java b/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java index f7dfa2d2cf0..c4cd4ec3cca 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java @@ -26,8 +26,8 @@ import java.util.HashSet; import java.util.Set; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; /** * After this visitor visits a tree, {@link #getTrees} returns all the trees that should match with diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java index 8e0299ad7fb..2882e95f8c1 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java @@ -161,11 +161,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; /** * A visitor that processes javac trees and JavaParser nodes simultaneously, matching corresponding diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java b/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java index eb82e041898..1ae0cd41f57 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java @@ -9,9 +9,9 @@ import java.util.function.BiFunction; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; /** * A class that visits each result expression of a switch expression and calls {@link diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index a1fbdd1d565..d0f3cfbe6cb 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -88,11 +88,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signature.qual.FullyQualifiedName; import org.checkerframework.dataflow.qual.Pure; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; import org.plumelib.util.CollectionsPlume; import org.plumelib.util.UniqueIdMap; @@ -2220,13 +2220,14 @@ public static boolean sameTree(ExpressionTree expr1, ExpressionTree expr2) { } /** - * Returns true if this is the default case for a switch statement or expression. + * Returns true if this is the default case for a switch statement or expression. (Also, returns + * true if {@code caseTree} is {@code case null, default:}.) * * @param caseTree a case tree * @return true if {@code caseTree} is the default case for a switch statement or expression */ public static boolean isDefaultCaseTree(CaseTree caseTree) { - return CaseUtils.getLabels(caseTree).isEmpty(); + return CaseUtils.isDefaultCaseTree(caseTree); } /** diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java similarity index 93% rename from javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java rename to javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java index 73d5f70333b..54cc6b10b5e 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java @@ -1,4 +1,4 @@ -package org.checkerframework.javacutil.trees; +package org.checkerframework.javacutil; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ExpressionTree; @@ -13,8 +13,6 @@ import javax.lang.model.SourceVersion; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signature.qual.ClassGetName; -import org.checkerframework.javacutil.BugInCF; -import org.checkerframework.javacutil.TreeUtils; /** * This class contains util methods for reflective accessing Tree classes and methods that were @@ -116,14 +114,39 @@ public static boolean isCaseRule(CaseTree caseTree) { } /** - * Get the list of labels from a case expression. For {@code default}, this is empty. Otherwise, - * in JDK 11 and earlier, this is a singleton list of expression trees. In JDK 12, this is a - * list of expression trees. In JDK 21+, this is a list of expression and pattern trees. + * Get the list of labels from a case expression. For {@code default}, this is empty. For {@code + * case null, default}, the list contains {@code null}. Otherwise, in JDK 11 and earlier, this + * is a list of a single expression tree. In JDK 12+, the list may have multiple expression + * trees. In JDK 21+, the list might contain a single pattern tree. * * @param caseTree the case expression to get the labels from * @return the list of case labels in the case */ public static List getLabels(CaseTree caseTree) { + return getLabels(caseTree, false); + } + + /** + * Returns true if this is the default case for a switch statement or expression. (Also, returns + * true if {@code caseTree} is {@code case null, default:}.) + * + * @param caseTree a case tree + * @return true if {@code caseTree} is the default case for a switch statement or expression + */ + public static boolean isDefaultCaseTree(CaseTree caseTree) { + if (sourceVersionNumber >= 21) { + for (Tree label : getLabels(caseTree, true)) { + if (TreeUtils.isDefaultCaseLabelTree(label)) { + return true; + } + } + return false; + } else { + return getExpressions(caseTree).isEmpty(); + } + } + + private static List getLabels(CaseTree caseTree, boolean useDefault) { if (sourceVersionNumber >= 21) { if (GET_LABELS == null) { GET_LABELS = getMethod(CaseTree.class, "getLabels"); @@ -134,7 +157,9 @@ public static List getLabels(CaseTree caseTree) { List labels = new ArrayList<>(); for (Tree caseLabel : caseLabelTrees) { if (TreeUtils.isDefaultCaseLabelTree(caseLabel)) { - return Collections.emptyList(); + if (useDefault) { + labels.add(caseLabel); + } } else if (TreeUtils.isConstantCaseLabelTree(caseLabel)) { labels.add(ConstantCaseLabelUtils.getConstantExpression(caseLabel)); } else if (TreeUtils.isPatternCaseLabelTree(caseLabel)) { @@ -184,7 +209,7 @@ public static List getExpressions(CaseTree caseTree) { if (GET_GUARD == null) { GET_GUARD = getMethod(CaseTree.class, "getGuard"); } - return (ExpressionTree) invokeNonNullResult(GET_GUARD, caseTree); + return (ExpressionTree) invoke(GET_GUARD, caseTree); } } From 69548e87a6cd959cd2d67f0e4155e63aef72439c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 12:10:56 -0700 Subject: [PATCH 87/91] Revert "Code review tweaks" This reverts commit 285bd8c55975c53ca904bece3b3c665cc92a8e9d. --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index fe0beceb586..7b30b34f61a 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -620,7 +620,7 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { * Visit a DeconstructionPatternTree. * * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree so the Checker - * Framework compiles under JDK 20 and earlier + * Framework compiles under JDK < 21 * @param p an unused parameter * @return the result of visiting the tree */ @@ -2357,6 +2357,7 @@ private SwitchBuilder(Tree switchTree) { // Build CFG for the cases. int defaultIndex = -1; + boolean exhaustiveAndNoDefault = exhaustiveAndNoDefault(); for (int i = 0; i < numCases; ++i) { CaseTree caseTree = caseTrees.get(i); if (TreeUtils.isDefaultCaseTree(caseTree)) { @@ -2365,9 +2366,7 @@ private SwitchBuilder(Tree switchTree) { // build the default case last. defaultIndex = i; } else { - boolean isLastCase = (i == numCases - 1); - boolean isLastCaseAndNoDefault = isLastCase && defaultIndex == -1; - boolean isLastCaseOfExhaustive = isLastCaseAndNoDefault && exhaustiveAndNoDefault(); + boolean isLastCaseOfExhaustive = (i == numCases - 1) && exhaustiveAndNoDefault; buildCase(caseTree, i, isLastCaseOfExhaustive); } } From 7ace7d766a2c32686771a3bec2c34097d1b20dc8 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 12:11:41 -0700 Subject: [PATCH 88/91] Put back a small change from the previously reverted commit. --- .../dataflow/cfg/builder/CFGTranslationPhaseOne.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 7b30b34f61a..24b221ed893 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -620,7 +620,7 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { * Visit a DeconstructionPatternTree. * * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree so the Checker - * Framework compiles under JDK < 21 + * Framework compiles under JDK 20 and earlier * @param p an unused parameter * @return the result of visiting the tree */ From f0ec233a5516b99541a94c645cff1304e9c5b09c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 12:28:19 -0700 Subject: [PATCH 89/91] Try to make code clearer. --- .../cfg/builder/CFGTranslationPhaseOne.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 24b221ed893..c8665ea8d8a 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -2357,7 +2357,6 @@ private SwitchBuilder(Tree switchTree) { // Build CFG for the cases. int defaultIndex = -1; - boolean exhaustiveAndNoDefault = exhaustiveAndNoDefault(); for (int i = 0; i < numCases; ++i) { CaseTree caseTree = caseTrees.get(i); if (TreeUtils.isDefaultCaseTree(caseTree)) { @@ -2365,9 +2364,11 @@ private SwitchBuilder(Tree switchTree) { // before the default case, no matter where `default:` is written. Therefore, // build the default case last. defaultIndex = i; + } else if (i == numCases - 1 && defaultIndex == -1) { + // This is the last case, and it's not a default case. + buildCase(caseTree, i, exhaustiveIgnoreDefault()); } else { - boolean isLastCaseOfExhaustive = (i == numCases - 1) && exhaustiveAndNoDefault; - buildCase(caseTree, i, isLastCaseOfExhaustive); + buildCase(caseTree, i, false); } } @@ -2570,19 +2571,11 @@ private void buildCase(CaseTree caseTree, int index, boolean isLastCaseOfExhaust } /** - * Returns true if the switch is exhaustive and does not contain a default case. + * Returns true if the switch is exhaustive; ignoring any default case * - * @return true if the switch is exhaustive and does not contain a default case + * @return true if the switch is exhaustive; ignoring any default case */ - private boolean exhaustiveAndNoDefault() { - for (CaseTree caseTree : caseTrees) { - if (TreeUtils.isDefaultCaseTree(caseTree)) { - return false; - } - } - - // There is no default case. Check whether the switch is exhaustive. - + private boolean exhaustiveIgnoreDefault() { // Switch expressions are always exhaustive, but they might have a default case, which is why // the above loop is not fused with the below loop. if (!TreeUtils.isSwitchStatement(switchTree)) { From cda38b27791efd7df91f55c226c7af12d19c132c Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 27 Sep 2023 13:28:52 -0700 Subject: [PATCH 90/91] Add Javadoc. --- .../javacutil/TreeUtilsAfterJava11.java | 53 +++++++++++++------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java index 54cc6b10b5e..b780e617d47 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java @@ -113,19 +113,6 @@ public static boolean isCaseRule(CaseTree caseTree) { return (Tree) invoke(GET_BODY, caseTree); } - /** - * Get the list of labels from a case expression. For {@code default}, this is empty. For {@code - * case null, default}, the list contains {@code null}. Otherwise, in JDK 11 and earlier, this - * is a list of a single expression tree. In JDK 12+, the list may have multiple expression - * trees. In JDK 21+, the list might contain a single pattern tree. - * - * @param caseTree the case expression to get the labels from - * @return the list of case labels in the case - */ - public static List getLabels(CaseTree caseTree) { - return getLabels(caseTree, false); - } - /** * Returns true if this is the default case for a switch statement or expression. (Also, returns * true if {@code caseTree} is {@code case null, default:}.) @@ -146,7 +133,43 @@ public static boolean isDefaultCaseTree(CaseTree caseTree) { } } - private static List getLabels(CaseTree caseTree, boolean useDefault) { + /** + * Get the list of labels from a case expression. For {@code default}, this is empty. For {@code + * case null, default}, the list contains {@code null}. Otherwise, in JDK 11 and earlier, this + * is a list of a single expression tree. In JDK 12+, the list may have multiple expression + * trees. In JDK 21+, the list might contain a single pattern tree. + * + * @param caseTree the case expression to get the labels from + * @return the list of case labels in the case + */ + public static List getLabels(CaseTree caseTree) { + return getLabels(caseTree, false); + } + + /** + * Get the list of labels from a case expression. + * + *

For JDKs before 21, if {@code caseTree} is the default case, then the returned list is + * empty. + * + *

For 21+ JDK, if {@code useDefaultCaseLabelTree} is false, then if {@code caseTree} is the + * default case or {@code case null, default}, then the returned list is empty. If {@code + * useDefaultCaseLabelTree} is true, then if {@code caseTree} is the default case the returned + * contains just a {@code DefaultCaseLabelTree}. If {@code useDefaultCaseLabelTree} is false, + * then if {@code caseTree} is {@code case null, default} the returned list is a {@code + * DefaultCaseLabelTree} and the expression tree for {@code null}. + * + *

Otherwise, in JDK 11 and earlier, this is a list of a single expression tree. In JDK 12+, + * the list may have multiple expression trees. In JDK 21+, the list might contain a single + * pattern tree. + * + * @param caseTree the case expression to get the labels from + * @param useDefaultCaseLabelTree weather the result should contain a {@code + * DefaultCaseLabelTree}. + * @return the list of case labels in the case + */ + private static List getLabels( + CaseTree caseTree, boolean useDefaultCaseLabelTree) { if (sourceVersionNumber >= 21) { if (GET_LABELS == null) { GET_LABELS = getMethod(CaseTree.class, "getLabels"); @@ -157,7 +180,7 @@ private static List getLabels(CaseTree caseTree, boolean useDefa List labels = new ArrayList<>(); for (Tree caseLabel : caseLabelTrees) { if (TreeUtils.isDefaultCaseLabelTree(caseLabel)) { - if (useDefault) { + if (useDefaultCaseLabelTree) { labels.add(caseLabel); } } else if (TreeUtils.isConstantCaseLabelTree(caseLabel)) { From 87a46042fc255662fdc2ebf3d3396ff5e95461c6 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Thu, 28 Sep 2023 13:28:42 -0700 Subject: [PATCH 91/91] Check for var decl more places. --- .../framework/type/TypeFromMemberVisitor.java | 6 ++++-- framework/tests/all-systems/java21/JEP441.java | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java index f5d44fa2b5d..c6ed843ddb2 100644 --- a/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/type/TypeFromMemberVisitor.java @@ -43,7 +43,8 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp result = f.getAnnotatedType(variableTree.getInitializer()); // Let normal defaulting happen for the primary annotation. result.clearPrimaryAnnotations(); - } else if (variableTree.getType() == null) { + } else if (TreeUtils.isVariableTreeDeclaredUsingVar(variableTree) + || variableTree.getType() == null) { // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. result = f.type(variableTree); } else { @@ -78,7 +79,8 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp // The underlying type of result does not have all annotations, but the TypeMirror of // variableTree.getType() does. // VariableTree#getType returns null for binding variables from a DeconstructionPatternTree. - if (variableTree.getType() != null) { + if (variableTree.getType() != null + && !TreeUtils.isVariableTreeDeclaredUsingVar(variableTree)) { DeclaredType declaredType = (DeclaredType) TreeUtils.typeOf(variableTree.getType()); AnnotatedTypes.applyAnnotationsFromDeclaredType(annotatedDeclaredType, declaredType); } diff --git a/framework/tests/all-systems/java21/JEP441.java b/framework/tests/all-systems/java21/JEP441.java index 43de14c4572..051c7142728 100644 --- a/framework/tests/all-systems/java21/JEP441.java +++ b/framework/tests/all-systems/java21/JEP441.java @@ -209,6 +209,16 @@ static void first(Object obj) { } } + record MyPair(S fst, T snd){}; + + static void recordInference(MyPair pair) { + switch (pair) { + case MyPair(var f, var s) -> { + String ff = f; + Integer ss = s; + } + } + } void fragment( Integer i ){ // TODO: This would be a good test case for the Value Checker. // switch (i) {