From f8b3aa006f0da4ff0a25241bc815017eff133346 Mon Sep 17 00:00:00 2001 From: Werner Dietl Date: Wed, 18 Oct 2023 13:42:57 +0200 Subject: [PATCH 01/27] typetools/checker-framework 3.39.0 release (#602) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Suzanne Millstein Co-authored-by: Martin Kellogg Co-authored-by: Michael Ernst Co-authored-by: Suzanne Millstein Co-authored-by: Michael Ernst Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: James Yoo <24359440+jyoo980@users.noreply.github.com> Co-authored-by: Calvin Loncaric Co-authored-by: Martin Kellogg --- azure-pipelines.yml | 263 +++++--- build.gradle | 128 ++-- .../qual/EnsuresCalledMethods.java | 4 +- checker/bin-devel/Dockerfile-README | 5 +- checker/bin-devel/Dockerfile-ubuntu-jdk11 | 7 +- .../bin-devel/Dockerfile-ubuntu-jdk11-plus | 5 +- checker/bin-devel/Dockerfile-ubuntu-jdk17 | 3 +- .../bin-devel/Dockerfile-ubuntu-jdk17-plus | 3 +- checker/bin-devel/Dockerfile-ubuntu-jdk20 | 6 +- .../bin-devel/Dockerfile-ubuntu-jdk20-plus | 4 +- checker/bin-devel/Dockerfile-ubuntu-jdk21 | 64 ++ .../bin-devel/Dockerfile-ubuntu-jdk21-plus | 100 ++++ checker/bin-devel/build.sh | 10 +- checker/bin-devel/test-cftests-all.sh | 1 + .../bin-devel/test-cftests-inference-part1.sh | 1 + .../bin-devel/test-cftests-inference-part2.sh | 1 + checker/bin-devel/test-cftests-inference.sh | 1 + checker/bin-devel/test-cftests-junit.sh | 1 + checker/bin-devel/test-cftests-nonjunit.sh | 1 + checker/bin-devel/test-daikon-part1.sh | 1 + checker/bin-devel/test-daikon-part2.sh | 1 + checker/bin-devel/test-daikon.sh | 1 + checker/bin-devel/test-downstream.sh | 1 + checker/bin-devel/test-guava-formatter.sh | 1 + checker/bin-devel/test-guava-index.sh | 1 + checker/bin-devel/test-guava-interning.sh | 1 + checker/bin-devel/test-guava-lock.sh | 1 + checker/bin-devel/test-guava-nullness.sh | 1 + checker/bin-devel/test-guava-regex.sh | 1 + checker/bin-devel/test-guava-signature.sh | 1 + checker/bin-devel/test-guava.sh | 1 + checker/bin-devel/test-misc.sh | 1 + checker/bin-devel/test-plume-lib.sh | 1 + checker/bin-devel/test-typecheck-part1.sh | 1 + checker/bin-devel/test-typecheck-part2.sh | 1 + checker/bin-devel/test-typecheck.sh | 1 + checker/bin-devel/wpi-plumelib/README | 14 +- .../bin-devel/wpi-plumelib/bcel-util.expected | 2 +- .../wpi-plumelib/html-pretty-print.expected | 1 - .../wpi-plumelib/test-wpi-plumelib.sh | 1 + checker/bin/wpi-many.sh | 51 +- checker/bin/wpi-summary.sh | 8 +- checker/bin/wpi.sh | 37 +- checker/build.gradle | 3 + .../calledmethods/builder/LombokSupport.java | 4 +- .../fenum/FenumAnnotatedTypeFactory.java | 6 +- .../checker/fenum/FenumVisitor.java | 11 +- .../checker/index/IndexMethodIdentifier.java | 2 +- .../LessThanAnnotatedTypeFactory.java | 3 +- .../checker/interning/InterningVisitor.java | 1 + .../MustCallAnnotatedTypeFactory.java | 67 ++- .../checker/mustcall/MustCallVisitor.java | 31 - .../nullness/NullnessNoInitVisitor.java | 18 +- .../MustCallConsistencyAnalyzer.java | 2 +- ...renceLogic.java => MustCallInference.java} | 10 +- .../ResourceLeakAnnotatedTypeFactory.java | 26 +- .../resourceleak/ResourceLeakVisitor.java | 37 +- .../AinferTestAnnotatedTypeFactory.java | 10 +- .../disbaruse/DisbarUseTypeFactory.java | 6 + checker/tests/index/Issue2029.java | 1 + checker/tests/index/LessThanBug.java | 15 + checker/tests/index/LessThanValue.java | 2 + checker/tests/nullness/java17/Issue5967.java | 24 + .../nullness/java17/SwitchTestIssue5412.java | 2 +- checker/tests/nullness/java21/FlowSwitch.java | 98 +++ .../nullness/java21/SimpleCaseGuard.java | 31 + .../resourceleak/MultipleOwnedResources.java | 29 + ...ultipleOwnedResourcesOfDifferentTypes.java | 44 ++ checker/tests/wpi-many/README.md | 4 + checker/tests/wpi-many/testin.txt | 12 +- dataflow/build.gradle | 3 +- .../cfg/builder/CFGTranslationPhaseOne.java | 164 +++-- .../cfg/node/AbstractNodeVisitor.java | 5 + .../dataflow/cfg/node/CaseNode.java | 15 + .../cfg/node/DeconstructorPatternNode.java | 98 +++ .../dataflow/cfg/node/InstanceOfNode.java | 54 +- .../dataflow/cfg/node/NodeVisitor.java | 9 + .../cfg/visualize/CFGVisualizeLauncher.java | 9 +- docs/CHANGELOG.md | 26 +- docs/checker-framework-webpage.html | 10 +- .../release/README-release-process.html | 8 +- docs/developer/release/langtools-excludes | 3 - docs/developer/release/release_build.py | 13 +- docs/developer/release/release_utils.py | 8 +- docs/developer/release/release_vars.py | 3 - docs/examples/errorprone/Makefile | 2 +- docs/examples/errorprone/build.gradle | 2 +- .../src/main/java/{ => com/example}/Demo.java | 2 + docs/examples/lombok/Makefile | 6 + docs/examples/lombok/build.gradle | 8 +- docs/manual/contributors.tex | 1 + docs/manual/external-tools.tex | 4 +- docs/manual/inference.tex | 51 +- docs/manual/manual.tex | 4 +- docs/manual/troubleshooting.tex | 4 +- docs/tutorial/index.html | 2 +- .../webpages/encryption-checker-cmd.html | 2 +- docs/tutorial/webpages/get-started-cmd.html | 2 +- .../tutorial/webpages/security-error-cmd.html | 2 +- docs/tutorial/webpages/user-input-cmd.html | 2 +- .../test/AinferValidatePerDirectoryTest.java | 2 +- .../CheckerFrameworkPerDirectoryTest.java | 4 + .../framework/test/TestUtilities.java | 8 +- framework/build.gradle | 12 +- .../common/aliasing/AliasingVisitor.java | 22 +- .../common/basetype/BaseTypeVisitor.java | 39 +- .../value/ValueAnnotatedTypeFactory.java | 77 ++- .../common/value/ValueQualifierHierarchy.java | 24 + .../WholeProgramInference.java | 5 +- .../WholeProgramInferenceImplementation.java | 32 +- ...holeProgramInferenceJavaParserStorage.java | 45 +- .../WholeProgramInferenceScenesStorage.java | 20 +- .../WholeProgramInferenceStorage.java | 9 +- .../framework/ajava/ExpectedTreesVisitor.java | 6 +- .../ajava/JointJavacJavaParserVisitor.java | 20 +- .../ajava/TreeScannerWithDefaults.java | 19 +- .../framework/flow/CFAbstractTransfer.java | 20 +- .../framework/source/SourceChecker.java | 67 ++- .../stub/AnnotationFileElementTypes.java | 104 +++- .../framework/stub/AnnotationFileParser.java | 29 +- .../framework/type/AnnotatedTypeFactory.java | 6 +- .../type/AnnotatedTypeParameterBounds.java | 7 +- .../framework/type/AnnotationClassLoader.java | 26 +- .../type/GenericAnnotatedTypeFactory.java | 6 +- .../framework/type/QualifierHierarchy.java | 8 +- .../framework/type/TypeFromMemberVisitor.java | 15 +- .../framework/util/CheckerMain.java | 20 +- ...erCompoundCheckerAnnotatedTypeFactory.java | 5 + .../FlowExpressionAnnotatedTypeFactory.java | 5 + .../h1h2checker/H1H2AnnotatedTypeFactory.java | 5 + .../tests/all-systems/java21/Issue6173.java | 26 + .../tests/all-systems/java21/JEP440.java | 76 +++ .../tests/all-systems/java21/JEP441.java | 230 +++++++ .../all-systems/java8/lambda/Issue573.java | 3 - .../tests/annotationclassloader/Makefile | 6 +- framework/tests/value/ValueOpt.java | 16 + gradlew | 4 +- javacutil/build.gradle | 4 + .../javacutil/AnnotationBuilder.java | 1 + .../javacutil/SwitchExpressionScanner.java | 10 +- .../javacutil/SystemUtil.java | 39 +- .../checkerframework/javacutil/TreeUtils.java | 368 +++--------- .../javacutil/TreeUtilsAfterJava11.java | 560 ++++++++++++++++++ 143 files changed, 2862 insertions(+), 908 deletions(-) create mode 100644 checker/bin-devel/Dockerfile-ubuntu-jdk21 create mode 100644 checker/bin-devel/Dockerfile-ubuntu-jdk21-plus rename checker/src/main/java/org/checkerframework/checker/resourceleak/{MustCallInferenceLogic.java => MustCallInference.java} (95%) create mode 100644 checker/tests/index/LessThanBug.java create mode 100644 checker/tests/nullness/java17/Issue5967.java create mode 100644 checker/tests/nullness/java21/FlowSwitch.java create mode 100644 checker/tests/nullness/java21/SimpleCaseGuard.java create mode 100644 checker/tests/resourceleak/MultipleOwnedResources.java create mode 100644 checker/tests/resourceleak/MultipleOwnedResourcesOfDifferentTypes.java create mode 100644 dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java rename docs/examples/errorprone/src/main/java/{ => com/example}/Demo.java (89%) create mode 100644 framework/tests/all-systems/java21/Issue6173.java create mode 100644 framework/tests/all-systems/java21/JEP440.java create mode 100644 framework/tests/all-systems/java21/JEP441.java create mode 100644 framework/tests/value/ValueOpt.java create mode 100644 javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a30b2c61f72..9ae8a240267 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,24 +10,29 @@ pr: jobs: +# Tests run on +# * "LTS" releases (8, 11, 17, 21), +# * jdk_latest, which is the latest JDK release. This is currently JDK 21, so not separate. +# (jdk_latest will move to JDK 22 once it is released.) +# * jdk_next, which is the next JDK version, which is not yet released and might still fail. +# This is currently JDK 22. +# # The dependsOn clauses are: -# * Everything depends on the canary jobs (the 4 main jdk17 jobs), except those jobs themselves. -# * Anything *_jdk8, *_jdk11, or *_jdk19 depends on *_jdk17. -# * Anything daikon_* and guava_* depends on the framework jobs (all core CF tests for JDK 8, 11, and 17). +# * Everything depends on the canary jobs (the 4 main jdk_21 jobs (the newest LTS release)), except those jobs themselves. +# * Anything *_jdk8, *_jdk11, *_jdk17, *_jdk_latest, or *_jdk_next depends on *_jdk21. +# * Anything daikon_* and guava_* depends on the framework jobs (all core CF tests for JDK 8, 11, 17, and 21). # (This avoids running these long-running jobs if something already failed in the framework.) # Include misc_jdk_latest 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 -# - misc_jdk_latest - - jspecify_jdk17 + - junit_jdk21 + - nonjunit_jdk21 + - inference_part1_jdk21 + - inference_part2_jdk21 + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 + - misc_jdk21 pool: vmImage: 'ubuntu-latest' steps: @@ -48,8 +53,7 @@ jobs: # - nonjunit_jdk_next - inference_jdk8 - inference_jdk11 - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_jdk17 - inference_jdk_latest # - inference_jdk_next - typecheck_jdk8 @@ -115,7 +119,7 @@ jobs: - job: junit_jdk11 dependsOn: - canary_jobs - - junit_jdk17 + - junit_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -130,6 +134,9 @@ jobs: artifactName: cf_jdk11 artifactType: pipeline - job: junit_jdk17 + dependsOn: + - canary_jobs + - junit_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk17:latest @@ -144,13 +151,10 @@ jobs: targetPath: ./checker/dist artifactName: cf_jdk17 artifactType: pipeline -- job: junit_jdk_latest - dependsOn: - - canary_jobs - - junit_jdk17 +- job: junit_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest:latest + container: wmdietl/cf-ubuntu-jdk21:latest timeoutInMinutes: 70 steps: - checkout: self @@ -165,7 +169,7 @@ jobs: - job: junit_jdk_next dependsOn: - canary_jobs - - junit_jdk17 + - junit_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk-next:latest @@ -196,7 +200,7 @@ jobs: - job: nonjunit_jdk11 dependsOn: - canary_jobs - - nonjunit_jdk17 + - nonjunit_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -206,6 +210,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: wmdietl/cf-ubuntu-jdk17:latest @@ -214,13 +221,10 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-cftests-nonjunit.sh displayName: test-cftests-nonjunit.sh -- job: nonjunit_jdk_latest - dependsOn: - - canary_jobs - - nonjunit_jdk17 +- job: nonjunit_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest:latest + container: wmdietl/cf-ubuntu-jdk21:latest steps: - checkout: self fetchDepth: 25 @@ -229,7 +233,7 @@ jobs: - job: nonjunit_jdk_next dependsOn: - canary_jobs - - nonjunit_jdk17 + - nonjunit_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk-next:latest @@ -246,8 +250,7 @@ jobs: - job: inference_jdk8 dependsOn: - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk8:latest @@ -260,8 +263,7 @@ jobs: - job: inference_jdk11 dependsOn: - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -271,8 +273,10 @@ 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_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk17:latest @@ -280,37 +284,47 @@ jobs: steps: - checkout: self fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-inference-part1.sh - displayName: test-cftests-inference-part1.sh -- job: inference_part2_jdk17 + - 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: wmdietl/cf-ubuntu-jdk17:latest + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 90 steps: - checkout: self fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-inference-part2.sh - displayName: test-cftests-inference-part2.sh -- job: inference_jdk_latest - dependsOn: - - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - bash: ./checker/bin-devel/test-cftests-inference-part1.sh + displayName: test-cftests-inference-part1.sh +- job: inference_part2_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest:latest + container: mdernst/cf-ubuntu-jdk21:latest timeoutInMinutes: 90 steps: - checkout: self fetchDepth: 25 - - bash: ./checker/bin-devel/test-cftests-inference.sh - displayName: test-cftests-inference.sh + - bash: ./checker/bin-devel/test-cftests-inference-part2.sh + displayName: test-cftests-inference-part2.sh +# - job: inference_jdk_latest +# 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_jdk_next dependsOn: - canary_jobs - - inference_part1_jdk17 - - inference_part2_jdk17 + - inference_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk-next:latest @@ -325,7 +339,7 @@ jobs: - job: misc_jdk8 dependsOn: # - canary_jobs - - misc_jdk17 + - misc_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk8-plus:latest @@ -346,6 +360,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: wmdietl/cf-ubuntu-jdk17-plus:latest @@ -354,13 +371,10 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-misc.sh displayName: test-misc.sh -- job: misc_jdk_latest - dependsOn: - # - canary_jobs - - misc_jdk17 +- job: misc_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest-plus:latest + container: wmdietl/cf-ubuntu-jdk21-plus:latest steps: - checkout: self fetchDepth: 25 @@ -393,11 +407,23 @@ jobs: fetchDepth: 1000 - bash: ./checker/bin-devel/test-typecheck.sh displayName: test-typecheck.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 - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 + - typecheck_part1_jdk21 + - typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -406,43 +432,54 @@ 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_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk17:latest + container: wmdietl/cf-ubuntu-jdk17-plus:latest steps: - checkout: self fetchDepth: 1000 - - bash: ./checker/bin-devel/test-typecheck-part1.sh - displayName: test-typecheck-part1.sh -- job: typecheck_part2_jdk17 + - 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: wmdietl/cf-ubuntu-jdk17-plus:latest + container: mdernst/cf-ubuntu-jdk21-plus:latest steps: - checkout: self fetchDepth: 1000 - - bash: ./checker/bin-devel/test-typecheck-part2.sh - displayName: test-typecheck-part2.sh -- job: typecheck_jdk_latest - dependsOn: - - canary_jobs - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 + - bash: ./checker/bin-devel/test-typecheck-part1.sh + displayName: test-typecheck-part1.sh +- job: typecheck_part2_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest:latest + container: mdernst/cf-ubuntu-jdk21-plus:latest steps: - checkout: self fetchDepth: 1000 - - bash: ./checker/bin-devel/test-typecheck.sh - displayName: test-typecheck.sh + - 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_jdk_next dependsOn: - canary_jobs - - typecheck_part1_jdk17 - - typecheck_part2_jdk17 + - typecheck_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk-next:latest @@ -457,8 +494,7 @@ jobs: condition: false dependsOn: - framework_jobs - - daikon_part1_jdk17 - - daikon_part2_jdk17 + - daikon_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk8:latest @@ -473,8 +509,7 @@ jobs: condition: false dependsOn: - framework_jobs - - daikon_part1_jdk17 - - daikon_part2_jdk17 + - daikon_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -484,24 +519,37 @@ 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_jdk21 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk17: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: wmdietl/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: - framework_jobs pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk17:latest + container: wmdietl/cf-ubuntu-jdk21:latest timeoutInMinutes: 80 steps: - checkout: self @@ -544,7 +592,7 @@ jobs: condition: false dependsOn: - framework_jobs - - guava_jdk17 + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk8:latest @@ -558,7 +606,7 @@ jobs: condition: false dependsOn: - framework_jobs - - guava_jdk17 + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -571,9 +619,22 @@ jobs: - job: guava_jdk17 dependsOn: - framework_jobs + - guava_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/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: wmdietl/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 @@ -618,7 +679,7 @@ jobs: condition: false dependsOn: - canary_jobs - - plume_lib_jdk17 + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk8:latest @@ -632,7 +693,7 @@ jobs: condition: false dependsOn: - canary_jobs - - plume_lib_jdk17 + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk11:latest @@ -644,6 +705,7 @@ jobs: - job: plume_lib_jdk17 dependsOn: - canary_jobs + - plume_lib_jdk21 pool: vmImage: 'ubuntu-latest' container: wmdietl/cf-ubuntu-jdk17:latest @@ -652,13 +714,12 @@ jobs: fetchDepth: 25 - bash: ./checker/bin-devel/test-plume-lib.sh displayName: test-plume-lib.sh -- job: plume_lib_jdk_latest +- job: plume_lib_jdk21 dependsOn: - canary_jobs - - plume_lib_jdk17 pool: vmImage: 'ubuntu-latest' - container: wmdietl/cf-ubuntu-jdk-latest:latest + container: wmdietl/cf-ubuntu-jdk21:latest steps: - checkout: self fetchDepth: 25 @@ -758,7 +819,7 @@ jobs: # - job: downstream_jdk11 # dependsOn: # - canary_jobs -# - downstream_jdk17 +# - downstream_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: wmdietl/cf-ubuntu-jdk11:latest @@ -770,6 +831,7 @@ jobs: # - job: downstream_jdk17 # dependsOn: # - canary_jobs +# - downstream_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: wmdietl/cf-ubuntu-jdk17:latest @@ -778,10 +840,21 @@ jobs: # fetchDepth: 25 # - bash: ./checker/bin-devel/test-downstream.sh # displayName: test-downstream.sh -# - job: downstream_jdk_latest +# - job: downstream_jdk21 # dependsOn: # - canary_jobs -# - downstream_jdk17 +# 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_jdk21 # pool: # vmImage: 'ubuntu-latest' # container: wmdietl/cf-ubuntu-jdk-latest:latest diff --git a/build.gradle b/build.gradle index d689f27da86..3e2e3486d07 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ apply plugin: 'de.undercouch.download' // There is another `repositories { ... }` block below; if you change this one, change that one as well. repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/'} mavenCentral() } @@ -45,8 +46,10 @@ ext { isJava18 = JavaVersion.current() == JavaVersion.VERSION_18 isJava19 = JavaVersion.current() == JavaVersion.VERSION_19 isJava20 = JavaVersion.current() == JavaVersion.VERSION_20 + isJava21 = JavaVersion.current() == JavaVersion.VERSION_21 - isJava20plus = isJava20 + isJava21plus = isJava21 + isJava20plus = isJava20 || isJava21plus isJava19plus = isJava19 || isJava20plus isJava18plus = isJava18 || isJava19plus isJava17plus = isJava17 || isJava18plus @@ -55,13 +58,8 @@ ext { isJava14plus = isJava14 || isJava15plus isJava11plus = JavaVersion.current() >= JavaVersion.VERSION_11 - // As of 2022-04-22, delombok doesn't yet support JDK 20; see https://projectlombok.org/changelog . - // Keep the variable in case we need to disable lombok for a future Java release. - skipDelombok = JavaVersion.current() >= JavaVersion.VERSION_20 - - // 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 + // As of 2023-09-23, delombok doesn't yet support JDK 22; see https://projectlombok.org/changelog . + skipDelombok = JavaVersion.current() > JavaVersion.VERSION_21 parentDir = file("${rootDir}/../").absolutePath @@ -69,13 +67,10 @@ ext { // annotationTools = "${parentDir}/annotation-tools" // afu = "${annotationTools}/annotation-file-utilities" - stubparser = "${parentDir}/stubparser" - stubparserVersion = "3.25.5" - stubparserJar = "${stubparser}/javaparser-core/target/stubparser-${stubparserVersion}.jar" - jtregHome = "${parentDir}/jtreg" plumeScriptsHome = "${project(':checker').projectDir}/bin-devel/.plume-scripts" htmlToolsHome = "${project(':checker').projectDir}/bin-devel/.html-tools" + doLikeJavacHome = "${project(':checker').projectDir}/bin/.do-like-javac" javadocMemberLevel = JavadocMemberLevel.PROTECTED @@ -163,7 +158,7 @@ allprojects { // * any new checkers have been added, or // * backward-incompatible changes have been made to APIs or elsewhere. // To make a snapshot release: ./gradlew publish - version '3.38.0' + version '3.39.0' tasks.withType(JavaCompile).configureEach { options.fork = true @@ -179,6 +174,7 @@ allprojects { // Keep in sync with "repositories { ... }" block above. repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/'} mavenCentral() } @@ -270,6 +266,15 @@ allprojects { // added in Java 14, not Java 17. doNotFormat += ['**/java17/'] } + // 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 (!isJava21plus) { + doNotFormat += ['**/java21/'] + } + format 'misc', { // define the files to apply `misc` to @@ -380,6 +385,15 @@ allprojects { // Add standard javac options tasks.withType(JavaCompile) { compilationTask -> dependsOn(':installGitHooks') + boolean jdk17Compiler = project.getProperties().getOrDefault('useJdk17Compiler', false) + if (jdk17Compiler) { + // This uses the Java 17 compiler to compile all code. + // https://docs.gradle.org/current/userguide/toolchains.html + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(17) + } + } + // Sorting is commented out because it disables incremental compilation. // Uncomment when needed. // // Put source files in deterministic order, for debugging. @@ -435,14 +449,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(isJava21plus && !jdk17Compiler) { + // TODO: Ignore this-escape 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. - // -classfile: classgraph jar file and https://bugs.openjdk.org/browse/JDK-8190452 - '-Xlint:-options,-fallthrough,-classfile', + lint, '-Xlint', ] @@ -517,46 +535,6 @@ task cloneAndBuildDependencies(type: Exec, group: 'Build') { executable 'checker/bin-devel/build.sh' } -task maybeCloneAndBuildDependencies() { - // No group so it does not show up in the output of `gradlew tasks` - description 'Clones (or updates) and builds all dependencies if they are not present.' - onlyIf { - !file(stubparserJar).exists() - // The jdk repository is cloned via the copyAndMinimizeAnnotatedJdkFiles task that is run if - // the repository does not exist when building checker.jar. - } - - doFirst { - if (file(stubparser).exists()) { - exec { - workingDir stubparser - executable 'git' - args = ['pull', '-q'] - ignoreExitValue = true // because of the doLast block below - } - exec { - workingDir stubparser - executable "${stubparser}/.build-without-test.sh" - } - } else { - exec { - executable 'checker/bin-devel/build.sh' - } - } - } - doLast { - if (!file(stubparserJar).exists()) { - println "The contents of ${stubparser}/javaparser-core/target (which does not contain stubparser-${stubparserVersion}.jar!) are:" - exec { - workingDir "${stubparser}/javaparser-core/target" - executable 'ls' - ignoreExitValue = true - } - throw new RuntimeException('Can\'t find stubparser jar: ' + stubparserJar + '; are you using an out-of-date Checker Framework or Stubparser?') - } - } -} - task version(group: 'Documentation') { description 'Print Checker Framework version' doLast { @@ -832,6 +810,7 @@ def createCloneTask(taskName, url, directory, extraArgs = []) { createCloneTask('getPlumeScripts', 'https://github.com/eisop-plume-lib/plume-scripts.git', plumeScriptsHome) createCloneTask('getHtmlTools', 'https://github.com/plume-lib/html-tools.git', htmlToolsHome) +createCloneTask('getDoLikeJavac', 'https://github.com/kelloggm/do-like-javac.git', doLikeJavacHome) // No group so it does not show up in the output of `gradlew tasks` @@ -963,27 +942,26 @@ subprojects { [ '-Astubs=javax-lang-model-element-name.astub' ]) - createCheckTypeTask(project.name, 'NullnessOnlyAnnotatedFor', - 'org.checkerframework.checker.nullness.NullnessChecker', - [ - '-AskipUses=com\\.sun\\.*', - // If a file does not contain @AnnotatedFor("nullness"), all its routines are assumed to return @Nullable. - '-AuseConservativeDefaultsForUncheckedCode=source' - ]) createCheckTypeTask(project.name, 'Purity', 'org.checkerframework.framework.util.PurityChecker') createCheckTypeTask(project.name, 'ResourceLeak', 'org.checkerframework.checker.resourceleak.ResourceLeakChecker') createCheckTypeTask(project.name, 'Signature', 'org.checkerframework.checker.signature.SignatureChecker') - // These pass on some subprojects, which the `typecheck` task runs. - // TODO: Incrementally add @AnnotatedFor on more classes. - createCheckTypeTask(project.name, 'Nullness', - 'org.checkerframework.checker.nullness.NullnessChecker', - [ - '-AskipUses=com.sun.*', - '-AconservativeArgumentNullnessAfterInvocation=true' - ]) + + if (project.name.is('framework') || project.name.is('checker')) { + createCheckTypeTask(project.name, 'Nullness', + 'org.checkerframework.checker.nullness.NullnessChecker', + [ + '-AskipUses=com\\.sun\\.*', + // If a file does not contain @AnnotatedFor("nullness"), all its routines are assumed to return @Nullable. + '-AuseConservativeDefaultsForUncheckedCode=source' + ]) + } else { + createCheckTypeTask(project.name, 'Nullness', + 'org.checkerframework.checker.nullness.NullnessChecker', + ['-AskipUses=com\\.sun\\.*']) + } // Add jtregTests to framework and checker modules @@ -1005,7 +983,7 @@ subprojects { "-dir:${projectDir}/jtreg", "-workDir:${jtregOutput}/${name}/work", "-reportDir:${jtregOutput}/${name}/report", - '-verbose:error,fail', + '-verbose:error,fail,nopass', // Don't add debugging information // '-javacoptions:-g', '-keywords:!ignore', @@ -1189,7 +1167,7 @@ subprojects { description 'Run the Checker Framework on itself (part 2)' dependsOn('checkResourceLeak', 'checkSignature') if (project.name.is('framework') || project.name.is('checker')) { - dependsOn('checkNullnessOnlyAnnotatedFor', 'checkCompilerMessages') + dependsOn('checkCompilerMessages') } else { dependsOn('checkNullness') } diff --git a/checker-qual/src/main/java/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethods.java b/checker-qual/src/main/java/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethods.java index 6025b3dee54..99dd06de290 100644 --- a/checker-qual/src/main/java/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethods.java +++ b/checker-qual/src/main/java/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethods.java @@ -47,9 +47,9 @@ @Repeatable(EnsuresCalledMethods.List.class) public @interface EnsuresCalledMethods { /** - * The Java expressions to which the qualifier applies. + * The Java expressions that will have methods called on them. * - * @return the Java expressions to which the qualifier applies + * @return the Java expressions that will have methods called on them * @see org.checkerframework.framework.qual.EnsuresQualifier */ // Postconditions must use "value" as the name (conditional postconditions use "expression"). diff --git a/checker/bin-devel/Dockerfile-README b/checker/bin-devel/Dockerfile-README index bd790f9429b..52c1c896085 100644 --- a/checker/bin-devel/Dockerfile-README +++ b/checker/bin-devel/Dockerfile-README @@ -72,16 +72,15 @@ export PROJECT=cf create_upload_docker_image export OS=ubuntu -export JDKVER=jdk-next +export JDKVER=jdk21 export PROJECT=cf create_upload_docker_image export OS=ubuntu -export JDKVER=jdk-next-plus +export JDKVER=jdk21-plus export PROJECT=cf create_upload_docker_image - Use numbered JDK releases for versions that should be supported longer term. jdk-latest is for the latest release and jdk-next is for pre-releases of the upcoming release. diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11 b/checker/bin-devel/Dockerfile-ubuntu-jdk11 index 5637f620dd9..f5a93e7611a 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11 @@ -22,7 +22,11 @@ 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 \ +&& 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/ . RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy update \ @@ -36,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus index 70cbaec716b..1219990f2f6 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk11-plus @@ -22,7 +22,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 \ +&& 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/ . @@ -38,7 +40,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk17 b/checker/bin-devel/Dockerfile-ubuntu-jdk17 index dfd3dda719a..daa3d19b160 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk17 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk17 @@ -24,6 +24,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 \ @@ -36,7 +38,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus index 91a030ce8cf..09887156ec3 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk17-plus @@ -5,7 +5,7 @@ # See releases at https://hub.docker.com/_/ubuntu for available images. # See https://packages.ubuntu.com/search?suite=default§ion=all&arch=any&keywords=openjdk-20-jdk&searchon=names # to see what Ubuntu versions support a particular OpenJDK version. -FROM ubuntu:22.10 +FROM ubuntu MAINTAINER Werner Dietl # According to @@ -38,7 +38,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk20 b/checker/bin-devel/Dockerfile-ubuntu-jdk20 index 6a26fe65586..b458c32e011 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk20 +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk20 @@ -3,7 +3,7 @@ # (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).?20\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. # See releases at https://hub.docker.com/_/ubuntu @@ -28,6 +28,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ ca-certificates-java \ && aptitude -y install \ + openjdk-17-jdk \ openjdk-20-jdk RUN export DEBIAN_FRONTEND=noninteractive \ @@ -42,7 +43,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ @@ -50,7 +50,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-jdk20-plus index 6b3071ca267..b19b8270b51 100644 --- a/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk20-plus @@ -1,7 +1,7 @@ # Create a Docker image that is ready to run the full Checker Framework tests, # including building the manual and Javadoc, using OpenJDK 20. # 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).?20\b # "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. # See releases at https://hub.docker.com/_/ubuntu for available images. @@ -28,6 +28,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && aptitude -y install \ ca-certificates-java \ && aptitude -y install \ + openjdk-17-jdk \ openjdk-20-jdk RUN export DEBIAN_FRONTEND=noninteractive \ @@ -42,7 +43,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libcurl3-gnutls \ make \ maven \ - mercurial \ python3-distutils \ python3-requests \ unzip \ diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk21 b/checker/bin-devel/Dockerfile-ubuntu-jdk21 new file mode 100644 index 00000000000..1b1915312a0 --- /dev/null +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21 @@ -0,0 +1,64 @@ +# Create a Docker image that is ready to run the main Checker Framework tests, +# 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).?21\b + +# "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. +FROM ubuntu:23.10 +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 + +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/ . + +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 autoremove \ +&& apt-get clean \ +&& rm -rf /var/lib/apt/lists/* diff --git a/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus new file mode 100644 index 00000000000..d64487d6230 --- /dev/null +++ b/checker/bin-devel/Dockerfile-ubuntu-jdk21-plus @@ -0,0 +1,100 @@ +# Create a Docker image that is ready to run the full Checker Framework tests, +# 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).?21\b + +# "ubuntu" is the latest LTS release. "ubuntu:rolling" is the latest release. +FROM ubuntu:23.10 +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. +# * 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 + +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/ . + +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/build.sh b/checker/bin-devel/build.sh index 2d5fbe2597d..e5b85bca29e 100755 --- a/checker/bin-devel/build.sh +++ b/checker/bin-devel/build.sh @@ -60,12 +60,6 @@ fi # echo "... done: (cd ${AT} && ./.build-without-test.sh)" -## Build stubparser -"$PLUME_SCRIPTS/git-clone-related" ${DEBUG_FLAG} eisop stubparser -echo "Running: (cd ../stubparser/ && ./.build-without-test.sh)" -(cd ../stubparser/ && ./.build-without-test.sh) -echo "... done: (cd ../stubparser/ && ./.build-without-test.sh)" - # TODO: NullnessNullMarkedTest depends on JSpecify annotations. # Find a way to not run that test, to avoid this dependency and # instead only use ./test-jspecify.sh. @@ -96,7 +90,7 @@ fi (TERM=dumb timeout 300 ./gradlew --write-verification-metadata sha256 help --dry-run || \ (sleep 1m && ./gradlew --write-verification-metadata sha256 help --dry-run)) -echo "running \"./gradlew assembleForJavac\" for checker-framework" -./gradlew assembleForJavac --console=plain --warning-mode=all -s -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.http.connectionTimeout=60000 +echo "running \"./gradlew assemble\" for checker-framework" +./gradlew assemble --console=plain --warning-mode=all -s -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.http.connectionTimeout=60000 echo Exiting checker/bin-devel/build.sh in "$(pwd)" diff --git a/checker/bin-devel/test-cftests-all.sh b/checker/bin-devel/test-cftests-all.sh index 077943814ed..79e0fece0a8 100755 --- a/checker/bin-devel/test-cftests-all.sh +++ b/checker/bin-devel/test-cftests-all.sh @@ -9,6 +9,7 @@ set -o xtrace export SHELLOPTS echo "SHELLOPTS=${SHELLOPTS}" +export ORG_GRADLE_PROJECT_useJdk17Compiler=true SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-cftests-inference-part1.sh b/checker/bin-devel/test-cftests-inference-part1.sh index 9087c7df7e7..49754c197f6 100755 --- a/checker/bin-devel/test-cftests-inference-part1.sh +++ b/checker/bin-devel/test-cftests-inference-part1.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-cftests-inference-part2.sh b/checker/bin-devel/test-cftests-inference-part2.sh index 7835c6b1434..9c6ff892343 100755 --- a/checker/bin-devel/test-cftests-inference-part2.sh +++ b/checker/bin-devel/test-cftests-inference-part2.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-cftests-inference.sh b/checker/bin-devel/test-cftests-inference.sh index e251cbcd1cb..ffc79da5aa7 100755 --- a/checker/bin-devel/test-cftests-inference.sh +++ b/checker/bin-devel/test-cftests-inference.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-cftests-junit.sh b/checker/bin-devel/test-cftests-junit.sh index 3a800239899..68390c2dade 100755 --- a/checker/bin-devel/test-cftests-junit.sh +++ b/checker/bin-devel/test-cftests-junit.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090# In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-cftests-nonjunit.sh b/checker/bin-devel/test-cftests-nonjunit.sh index bea711bc86c..ef6a4ca364e 100755 --- a/checker/bin-devel/test-cftests-nonjunit.sh +++ b/checker/bin-devel/test-cftests-nonjunit.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090# In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-daikon-part1.sh b/checker/bin-devel/test-daikon-part1.sh index d44052bca44..9724320b74b 100755 --- a/checker/bin-devel/test-daikon-part1.sh +++ b/checker/bin-devel/test-daikon-part1.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090# In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-daikon-part2.sh b/checker/bin-devel/test-daikon-part2.sh index 8d0a12b77ad..8c72c351ce4 100755 --- a/checker/bin-devel/test-daikon-part2.sh +++ b/checker/bin-devel/test-daikon-part2.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-daikon.sh b/checker/bin-devel/test-daikon.sh index d81f60e1a1f..fcd8df3a14f 100755 --- a/checker/bin-devel/test-daikon.sh +++ b/checker/bin-devel/test-daikon.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-downstream.sh b/checker/bin-devel/test-downstream.sh index bd67d1e6750..9e99eb6d64c 100755 --- a/checker/bin-devel/test-downstream.sh +++ b/checker/bin-devel/test-downstream.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-formatter.sh b/checker/bin-devel/test-guava-formatter.sh index 15b6ed8b272..3f3280ee0de 100755 --- a/checker/bin-devel/test-guava-formatter.sh +++ b/checker/bin-devel/test-guava-formatter.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-index.sh b/checker/bin-devel/test-guava-index.sh index 587ea87ecd6..d8eff711376 100755 --- a/checker/bin-devel/test-guava-index.sh +++ b/checker/bin-devel/test-guava-index.sh @@ -9,6 +9,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-interning.sh b/checker/bin-devel/test-guava-interning.sh index e393a8b97dd..a80ecb93f86 100755 --- a/checker/bin-devel/test-guava-interning.sh +++ b/checker/bin-devel/test-guava-interning.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-lock.sh b/checker/bin-devel/test-guava-lock.sh index 18ddba087d3..66ec9c4e662 100755 --- a/checker/bin-devel/test-guava-lock.sh +++ b/checker/bin-devel/test-guava-lock.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-nullness.sh b/checker/bin-devel/test-guava-nullness.sh index 9e908e69c6d..5c677882e94 100755 --- a/checker/bin-devel/test-guava-nullness.sh +++ b/checker/bin-devel/test-guava-nullness.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-regex.sh b/checker/bin-devel/test-guava-regex.sh index 8d57874604a..8a11f68718f 100755 --- a/checker/bin-devel/test-guava-regex.sh +++ b/checker/bin-devel/test-guava-regex.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava-signature.sh b/checker/bin-devel/test-guava-signature.sh index 28b287a4b89..78ad7ff5539 100755 --- a/checker/bin-devel/test-guava-signature.sh +++ b/checker/bin-devel/test-guava-signature.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-guava.sh b/checker/bin-devel/test-guava.sh index cc6c6f45646..ca88627ebee 100755 --- a/checker/bin-devel/test-guava.sh +++ b/checker/bin-devel/test-guava.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh # TODO: Maybe I should move this into the CI job, and do it for all CI jobs. diff --git a/checker/bin-devel/test-misc.sh b/checker/bin-devel/test-misc.sh index 70c4642b73a..9404f3b1c4b 100755 --- a/checker/bin-devel/test-misc.sh +++ b/checker/bin-devel/test-misc.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh PLUME_SCRIPTS="$SCRIPTDIR/.plume-scripts" diff --git a/checker/bin-devel/test-plume-lib.sh b/checker/bin-devel/test-plume-lib.sh index b2488e4b53b..0363bdb5c4c 100755 --- a/checker/bin-devel/test-plume-lib.sh +++ b/checker/bin-devel/test-plume-lib.sh @@ -38,6 +38,7 @@ echo "PACKAGES=" "${PACKAGES[@]}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-typecheck-part1.sh b/checker/bin-devel/test-typecheck-part1.sh index b2d4d0c448c..547782e23ba 100755 --- a/checker/bin-devel/test-typecheck-part1.sh +++ b/checker/bin-devel/test-typecheck-part1.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-typecheck-part2.sh b/checker/bin-devel/test-typecheck-part2.sh index e8067ad79ca..4bbd499e4d2 100755 --- a/checker/bin-devel/test-typecheck-part2.sh +++ b/checker/bin-devel/test-typecheck-part2.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh diff --git a/checker/bin-devel/test-typecheck.sh b/checker/bin-devel/test-typecheck.sh index e25c97533cc..afed83e0522 100755 --- a/checker/bin-devel/test-typecheck.sh +++ b/checker/bin-devel/test-typecheck.sh @@ -8,6 +8,7 @@ echo "SHELLOPTS=${SHELLOPTS}" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # shellcheck disable=SC1090 # In newer shellcheck than 0.6.0, pass: "-P SCRIPTDIR" (literally) +export ORG_GRADLE_PROJECT_useJdk17Compiler=true source "$SCRIPTDIR"/build.sh 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/bin-devel/wpi-plumelib/bcel-util.expected b/checker/bin-devel/wpi-plumelib/bcel-util.expected index fe2182141d4..811aa279c6c 100644 --- a/checker/bin-devel/wpi-plumelib/bcel-util.expected +++ b/checker/bin-devel/wpi-plumelib/bcel-util.expected @@ -1,4 +1,4 @@ -BcelUtil.java:778: error: [argument] incompatible argument for parameter typename of ClassnameAndDimensions.parseFqBinaryName. +BcelUtil.java:764: error: [argument] incompatible argument for parameter typename of ClassnameAndDimensions.parseFqBinaryName. Signatures.ClassnameAndDimensions.parseFqBinaryName(classname); ^ found : @SignatureUnknown String diff --git a/checker/bin-devel/wpi-plumelib/html-pretty-print.expected b/checker/bin-devel/wpi-plumelib/html-pretty-print.expected index 79e90eb9b40..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 @@ -4 warnings diff --git a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh index 8a75788a53c..a8343ef2b18 100755 --- a/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh +++ b/checker/bin-devel/wpi-plumelib/test-wpi-plumelib.sh @@ -46,6 +46,7 @@ clean_compile_output() { # Remove uninteresting output sed -i '/^warning: \[path\] bad path element /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" diff --git a/checker/bin/wpi-many.sh b/checker/bin/wpi-many.sh index 2a2549752b4..62bf6583e32 100755 --- a/checker/bin/wpi-many.sh +++ b/checker/bin/wpi-many.sh @@ -80,6 +80,13 @@ 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" +else + has_java21="yes" +fi + if [ "${has_java_home}" = "yes" ] && [ ! -d "${JAVA_HOME}" ]; then echo "JAVA_HOME is set to a non-existent directory ${JAVA_HOME}" exit 1 @@ -103,6 +110,10 @@ if [ "${has_java_home}" = "yes" ]; 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" + fi fi if [ "${has_java8}" = "yes" ] && [ ! -d "${JAVA8_HOME}" ]; then @@ -125,17 +136,23 @@ if [ "${has_java20}" = "yes" ] && [ ! -d "${JAVA20_HOME}" ]; then exit 1 fi -if [ "${has_java8}" = "no" ] && [ "${has_java11}" = "no" ] && [ "${has_java17}" = "no" ] && [ "${has_java20}" = "no" ]; then +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" ] && [ "${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, 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 exit 1 @@ -187,6 +204,9 @@ do # Skip lines that start with "#". [[ $line = \#* ]] && continue + # Remove trailing return character if reading from a DOS file. + line="$(echo "$line" | tr -d '\r')" + REPOHASH=${line} REPO=$(echo "${REPOHASH}" | awk '{print $1}') @@ -266,7 +286,12 @@ do echo "wpi-many.sh about to call wpi.sh in $(pwd) at $(date)" /bin/bash -x "${SCRIPTDIR}/wpi.sh" -d "${REPO_FULLPATH}" -t "${TIMEOUT}" -g "${GRADLECACHEDIR}" -- "$@" &> "${OUTDIR}-results/wpi-out" "${DIR}/dljc-out/clean-output" + (eval "${CLEAN_CMD}" < /dev/null | tee -a "${DIR}/dljc-out/clean-output") || true + ls -al "${DIR}/dljc-out" + WPI_RESULTS_AVAILABLE="${WPI_RESULTS_AVAILABLE}"$'\n'"$(cat "${DIR}/dljc-out/clean-output")" return fi @@ -272,11 +292,10 @@ function configure_and_exec_dljc { cat "${dljc_stdout}" echo "=== End of DLJC standard out/err. ===" - # the wpi.py script in do-like-javac outputs the following text if no build/whole-program-inference directory + # The wpi.py script in do-like-javac outputs the following text if no build/whole-program-inference directory # 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}\"" 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} ===" @@ -310,12 +329,6 @@ stdout is in $dljc_stdout" # Clone or update DLJC if [ "${DLJC}" = "" ]; then # The user did not set the DLJC environment variable. - (cd "${SCRIPTDIR}"/../.. && (./gradlew --stacktrace getPlumeScripts || (sleep 60s && ./gradlew --stacktrace getPlumeScripts))) - "${SCRIPTDIR}"/../bin-devel/.plume-scripts/git-clone-related kelloggm do-like-javac "${SCRIPTDIR}"/.do-like-javac - if [ ! -d "${SCRIPTDIR}/.do-like-javac" ]; then - echo "Failed to clone do-like-javac" - exit 1 - fi DLJC="${SCRIPTDIR}/.do-like-javac/dljc" else # The user did set the DLJC environment variable. diff --git a/checker/build.gradle b/checker/build.gradle index 0482495fc5a..05fbf5973f1 100644 --- a/checker/build.gradle +++ b/checker/build.gradle @@ -150,6 +150,7 @@ task assembleForJavac(dependsOn: shadowJar, group: 'Build') { } assemble.dependsOn assembleForJavac +assemble.dependsOn(':getDoLikeJavac') task allSourcesJar(type: Jar, group: 'Build') { description 'Creates a sources jar that includes sources for all Checker Framework classes in checker.jar' @@ -934,6 +935,7 @@ task ainferTest(group: 'Verification') { task wpiManyTest(group: 'Verification') { description 'Tests the wpi-many.sh script (and indirectly the wpi.sh script). Requires an Internet connection.' dependsOn(assembleForJavac) + dependsOn(':getDoLikeJavac') // This test must always be re-run when requested. outputs.upToDateWhen { false } @@ -1062,6 +1064,7 @@ task wpiManyTest(group: 'Verification') { task wpiPlumeLibTest(group: 'Verification') { description 'Tests whole-program inference on the plume-lib projects. Requires an Internet connection.' dependsOn(assembleForJavac) + dependsOn(':getDoLikeJavac') // This test must always be re-run when requested. outputs.upToDateWhen { false } diff --git a/checker/src/main/java/org/checkerframework/checker/calledmethods/builder/LombokSupport.java b/checker/src/main/java/org/checkerframework/checker/calledmethods/builder/LombokSupport.java index 4d7e6211305..9807af5357f 100644 --- a/checker/src/main/java/org/checkerframework/checker/calledmethods/builder/LombokSupport.java +++ b/checker/src/main/java/org/checkerframework/checker/calledmethods/builder/LombokSupport.java @@ -209,5 +209,7 @@ private List getLombokRequiredProperties(Element lombokClassElement) { } @Override - public void handleConstructor(NewClassTree tree, AnnotatedTypeMirror type) {} + public void handleConstructor(NewClassTree tree, AnnotatedTypeMirror type) { + // do nothing + } } 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 c79a0583e6b..aa84913b0c2 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumAnnotatedTypeFactory.java @@ -77,7 +77,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/fenum/FenumVisitor.java b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java index 6433c847097..5b915fb934c 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java @@ -14,12 +14,20 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.javacutil.AnnotationMirrorSet; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; import java.util.List; import javax.lang.model.element.ExecutableElement; +/** The visitor for Fenum Checker. */ public class FenumVisitor extends BaseTypeVisitor { + + /** + * Creates a Fenum Visitor + * + * @param checker the checker + */ public FenumVisitor(BaseTypeChecker checker) { super(checker); } @@ -46,8 +54,7 @@ public Void visitSwitch(SwitchTree tree, Void p) { AnnotatedTypeMirror exprType = atypeFactory.getAnnotatedType(expr); for (CaseTree caseExpr : tree.getCases()) { - List realCaseExprs = - TreeUtils.caseTreeGetExpressions(caseExpr); + List realCaseExprs = CaseUtils.getExpressions(caseExpr); // Check all the case options against the switch expression type: for (ExpressionTree realCaseExpr : realCaseExprs) { AnnotatedTypeMirror caseType = atypeFactory.getAnnotatedType(realCaseExpr); diff --git a/checker/src/main/java/org/checkerframework/checker/index/IndexMethodIdentifier.java b/checker/src/main/java/org/checkerframework/checker/index/IndexMethodIdentifier.java index 50c4ef996bb..75d232b2db6 100644 --- a/checker/src/main/java/org/checkerframework/checker/index/IndexMethodIdentifier.java +++ b/checker/src/main/java/org/checkerframework/checker/index/IndexMethodIdentifier.java @@ -94,7 +94,7 @@ public IndexMethodIdentifier(AnnotatedTypeFactory atypeFactory) { * * @param methodTree the method invocation tree to be tested * @return true iff the argument is an invocation of one of String's indexOf or lastIndexOf - * methods that takes another string as a parameter. + * methods that takes another string as a parameter */ public boolean isIndexOfString(Tree methodTree) { ProcessingEnvironment processingEnv = atypeFactory.getProcessingEnv(); diff --git a/checker/src/main/java/org/checkerframework/checker/index/inequality/LessThanAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/index/inequality/LessThanAnnotatedTypeFactory.java index bc9d374abd0..641527d5231 100644 --- a/checker/src/main/java/org/checkerframework/checker/index/inequality/LessThanAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/index/inequality/LessThanAnnotatedTypeFactory.java @@ -185,10 +185,11 @@ public boolean isLessThan(AnnotationMirror left, String right) { * * @param smaller the first value to compare * @param bigger the second value to compare + * @param path used to parse expressions strings * @return {@code smaller < bigger}, using information from the Value Checker */ public boolean isLessThanByValue(Tree smaller, String bigger, TreePath path) { - Long smallerValue = ValueCheckerUtils.getMinValue(smaller, getValueAnnotatedTypeFactory()); + Long smallerValue = ValueCheckerUtils.getMaxValue(smaller, getValueAnnotatedTypeFactory()); if (smallerValue == null) { return false; } 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 dc72963e72c..eff70f42a86 100644 --- a/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/interning/InterningVisitor.java @@ -215,6 +215,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) diff --git a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallAnnotatedTypeFactory.java index ba70be9fb84..e4b1ee1b641 100644 --- a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallAnnotatedTypeFactory.java @@ -25,6 +25,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; +import org.checkerframework.framework.type.GenericAnnotatedTypeFactory; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.type.QualifierUpperBounds; import org.checkerframework.framework.type.SubtypeIsSubsetQualifierHierarchy; @@ -39,9 +40,11 @@ import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypeSystemError; +import org.checkerframework.javacutil.TypesUtils; import java.lang.annotation.Annotation; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; @@ -50,10 +53,12 @@ import java.util.Map; import java.util.Set; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; /** * The annotated type factory for the Must Call Checker. Primarily responsible for the subtyping @@ -354,7 +359,7 @@ private AnnotationMirror createMustCallImpl(List methodList) { @Override protected QualifierHierarchy createQualifierHierarchy() { - return new SubtypeIsSubsetQualifierHierarchy( + return new MustCallQualifierHierarchy( this.getSupportedTypeQualifiers(), this.getProcessingEnv(), this); } @@ -447,4 +452,64 @@ public Void visitIdentifier(IdentifierTree tree, AnnotatedTypeMirror type) { public @Nullable LocalVariableNode getTempVar(Node node) { return tempVars.get(node.getTree()); } + + /** + * Returns true if the given type should never have a must-call obligation. + * + * @param type the type to check + * @return true if the given type should never have a must-call obligation + */ + public boolean shouldHaveNoMustCallObligation(TypeMirror type) { + return type.getKind().isPrimitive() + || TypesUtils.isClass(type) + || TypesUtils.isString(type); + } + + /** Qualifier hierarchy for the Must Call Checker. */ + class MustCallQualifierHierarchy extends SubtypeIsSubsetQualifierHierarchy { + + /** + * Creates a SubtypeIsSubsetQualifierHierarchy from the given classes. + * + * @param qualifierClasses classes of annotations that are the qualifiers for this hierarchy + * @param processingEnv processing environment + * @param atypeFactory the associated type factory + */ + public MustCallQualifierHierarchy( + Collection> qualifierClasses, + ProcessingEnvironment processingEnv, + GenericAnnotatedTypeFactory atypeFactory) { + super(qualifierClasses, processingEnv, atypeFactory); + } + + @Override + public boolean isSubtypeShallow( + AnnotationMirror subQualifier, + TypeMirror subType, + AnnotationMirror superQualifier, + TypeMirror superType) { + if (shouldHaveNoMustCallObligation(subType) + || shouldHaveNoMustCallObligation(superType)) { + return true; + } + return super.isSubtypeShallow(subQualifier, subType, superQualifier, superType); + } + + @Override + public @Nullable AnnotationMirror leastUpperBoundShallow( + AnnotationMirror qualifier1, + TypeMirror tm1, + AnnotationMirror qualifier2, + TypeMirror tm2) { + boolean tm1NoMustCall = shouldHaveNoMustCallObligation(tm1); + boolean tm2NoMustCall = shouldHaveNoMustCallObligation(tm2); + if (tm1NoMustCall == tm2NoMustCall) { + return super.leastUpperBoundShallow(qualifier1, tm1, qualifier2, tm2); + } else if (tm1NoMustCall) { + return qualifier1; + } else { // if (tm2NoMustCall) { + return qualifier2; + } + } + } } diff --git a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java index 355a1e3c381..e9a0f2b5d87 100644 --- a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java @@ -25,7 +25,6 @@ import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.TypesUtils; import java.util.ArrayList; import java.util.Collections; @@ -321,10 +320,6 @@ protected boolean commonAssignmentCheck( @CompilerMessageKey String errorKey, Object... extraArgs) { - if (noMustCallObligation(varType) || noMustCallObligation(valueType)) { - return true; - } - if (commonAssignmentCheckOnResourceVariable) { commonAssignmentCheckOnResourceVariable = false; // The LHS has been marked as a resource variable. Skip the standard common assignment @@ -407,30 +402,4 @@ protected AnnotationMirrorSet getExceptionParameterLowerBoundAnnotations() { public Void visitAnnotation(AnnotationTree tree, Void p) { return null; } - - /** - * Returns true if the given type should never have a must-call obligation. - * - * @param atm the type to check - * @return true if the given type should never have a must-call obligation - */ - private boolean noMustCallObligation(AnnotatedTypeMirror atm) { - if (atm.getKind().isPrimitive()) { - return true; - } - TypeMirror tm = atm.getUnderlyingType(); - if (TypesUtils.isClass(tm) || TypesUtils.isString(tm)) { - return true; - } - return false; - } - - @Override - protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { - if (noMustCallObligation(castType) || noMustCallObligation(exprType)) { - return true; - } - - return super.isTypeCastSafe(castType, exprType); - } } diff --git a/checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitVisitor.java b/checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitVisitor.java index dcb48cbfc9d..7d3687f42ab 100644 --- a/checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitVisitor.java @@ -460,14 +460,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/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java b/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java index 52557668104..b31de5051a5 100644 --- a/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java @@ -141,7 +141,7 @@ * variables, the checker wouldn't be able to verify code such as {@code new Socket(host, * port).close()}, which would cause false positives. Temporaries are created for {@code new} * expressions, method calls (for the return value), and ternary expressions. Other types of - * expressions may also be supported in the future. + * expressions may be supported in the future. */ /*package-private*/ class MustCallConsistencyAnalyzer { diff --git a/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInferenceLogic.java b/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInference.java similarity index 95% rename from checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInferenceLogic.java rename to checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInference.java index 9bb772f8c6b..1f8e34bb5f4 100644 --- a/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInferenceLogic.java +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallInference.java @@ -28,7 +28,7 @@ * field if it finds a method that satisfies the @MustCall obligation of the field along some path * to the regular exit point. */ -public class MustCallInferenceLogic { +public class MustCallInference { /** The set of owning fields. */ private final Set owningFields = new HashSet<>(); @@ -46,14 +46,14 @@ public class MustCallInferenceLogic { private final ControlFlowGraph cfg; /** - * Creates a MustCallInferenceLogic. If the type factory has whole program inference enabled, - * its postAnalyze method should instantiate a new MustCallInferenceLogic using this constructor - * and then call {@link #runInference()}. + * Creates a MustCallInference. If the type factory has whole program inference enabled, its + * postAnalyze method should instantiate a new MustCallInference using this constructor and then + * call {@link #runInference()}. * * @param typeFactory the type factory * @param cfg the ControlFlowGraph */ - /*package-private*/ MustCallInferenceLogic( + /*package-private*/ MustCallInference( ResourceLeakAnnotatedTypeFactory typeFactory, ControlFlowGraph cfg) { this.typeFactory = typeFactory; this.cfg = cfg; diff --git a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakAnnotatedTypeFactory.java index ba6f003d10e..3667624435f 100644 --- a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakAnnotatedTypeFactory.java +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakAnnotatedTypeFactory.java @@ -62,6 +62,10 @@ public class ResourceLeakAnnotatedTypeFactory extends CalledMethodsAnnotatedType /*package-private*/ final ExecutableElement ensuresCalledMethodsMethodsElement = TreeUtils.getMethod(EnsuresCalledMethods.class, "methods", 0, processingEnv); + /** The EnsuresCalledMethods.List.value element/field. */ + private final ExecutableElement ensuresCalledMethodsListValueElement = + TreeUtils.getMethod(EnsuresCalledMethods.List.class, "value", 0, processingEnv); + /** The CreatesMustCallFor.List.value element/field. */ private final ExecutableElement createsMustCallForListValueElement = TreeUtils.getMethod(CreatesMustCallFor.List.class, "value", 0, processingEnv); @@ -142,13 +146,12 @@ public void postAnalyze(ControlFlowGraph cfg) { // Inferring owning annotations for final owning fields /* NO-AFU - if (getWholeProgramInference() != null) { - if (cfg.getUnderlyingAST().getKind() == UnderlyingAST.Kind.METHOD) { - MustCallInferenceLogic mustCallInferenceLogic = - new MustCallInferenceLogic(this, cfg); - mustCallInferenceLogic.runInference(); - } - } + if (getWholeProgramInference() != null) { + if (cfg.getUnderlyingAST().getKind() == UnderlyingAST.Kind.METHOD) { + MustCallInference mustCallInferenceLogic = new MustCallInference(this, cfg); + mustCallInferenceLogic.runInference(); + } + } */ super.postAnalyze(cfg); @@ -388,6 +391,15 @@ public boolean canCreateObligations() { return super.getTypeFactoryOfSubcheckerOrNull(subCheckerClass); } + /** + * Returns the {@link EnsuresCalledMethods.List#value} element. + * + * @return the {@link EnsuresCalledMethods.List#value} element + */ + public ExecutableElement getEnsuresCalledMethodsListValueElement() { + return ensuresCalledMethodsListValueElement; + } + /** * Returns the {@link CreatesMustCallFor#value} element. * diff --git a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakVisitor.java b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakVisitor.java index 61fc6e88fc9..f7c9a7a8f9a 100644 --- a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakVisitor.java @@ -13,6 +13,7 @@ import org.checkerframework.checker.mustcall.qual.Owning; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.dataflow.expression.JavaExpression; +import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.framework.flow.CFAbstractStore; import org.checkerframework.framework.flow.CFAbstractValue; import org.checkerframework.javacutil.AnnotationMirrorSet; @@ -322,6 +323,34 @@ private static String getCreatesMustCallForValue( return result; } + /** + * Get all {@link EnsuresCalledMethods} annotations on an element. + * + * @param elt an executable element that might have {@link EnsuresCalledMethods} annotations + * @param atypeFactory a ResourceLeakAnnotatedTypeFactory + * @return a set of {@link EnsuresCalledMethods} annotations + */ + @Pure + private static AnnotationMirrorSet getEnsuresCalledMethodsAnnotations( + ExecutableElement elt, ResourceLeakAnnotatedTypeFactory atypeFactory) { + AnnotationMirror ensuresCalledMethodsAnnos = + atypeFactory.getDeclAnnotation(elt, EnsuresCalledMethods.List.class); + AnnotationMirrorSet result = new AnnotationMirrorSet(); + if (ensuresCalledMethodsAnnos != null) { + result.addAll( + AnnotationUtils.getElementValueArray( + ensuresCalledMethodsAnnos, + atypeFactory.getEnsuresCalledMethodsListValueElement(), + AnnotationMirror.class)); + } + AnnotationMirror ensuresCalledMethod = + atypeFactory.getDeclAnnotation(elt, EnsuresCalledMethods.class); + if (ensuresCalledMethod != null) { + result.add(ensuresCalledMethod); + } + return result; + } + @Override public Void visitVariable(VariableTree tree, Void p) { Element varElement = TreeUtils.elementFromDeclaration(tree); @@ -389,11 +418,11 @@ private void checkOwningField(Element field) { if (siblingElement.getKind() == ElementKind.METHOD && enclosingMustCallValues.contains( siblingElement.getSimpleName().toString())) { - AnnotationMirror ensuresCalledMethodsAnno = - rlTypeFactory.getDeclAnnotation( - siblingElement, EnsuresCalledMethods.class); - if (ensuresCalledMethodsAnno != null) { + AnnotationMirrorSet allEnsuresCalledMethodsAnnos = + getEnsuresCalledMethodsAnnotations( + (ExecutableElement) siblingElement, rlTypeFactory); + for (AnnotationMirror ensuresCalledMethodsAnno : allEnsuresCalledMethodsAnnos) { List values = AnnotationUtils.getElementValueArray( ensuresCalledMethodsAnno, 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 3ba44303fef..385f1d9b6fc 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 @@ -85,6 +85,11 @@ public class AinferTestAnnotatedTypeFactory extends BaseAnnotatedTypeFactory { private final ExecutableElement siblingWithFieldsValue2Element = TreeUtils.getMethod(AinferSiblingWithFields.class, "value2", 0, processingEnv); + /** + * Creates an AinferTestAnnotatedTypeFactory. + * + * @param checker the checker + */ public AinferTestAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); // Support a declaration annotation that has the same meaning as @Sibling1, to test that the @@ -156,10 +161,11 @@ public Void visitMethod(MethodTree methodTree, AnnotatedTypeMirror type) { WholeProgramInference wpi = atypeFactory.getWholeProgramInference(); if (wpi != null) { ExecutableElement execElt = TreeUtils.elementFromDeclaration(methodTree); - for (int i = 0; i < execElt.getParameters().size(); ++i) { + int numParams = execElt.getParameters().size(); + for (int i = 0; i < numParams; ++i) { VariableElement param = execElt.getParameters().get(i); if (param.getSimpleName().contentEquals("iShouldBeTreatedAsSibling1")) { - wpi.addDeclarationAnnotationToFormalParameter(execElt, i, TREAT_AS_SIBLING1); + wpi.addDeclarationAnnotationToFormalParameter(execElt, i + 1, TREAT_AS_SIBLING1); } } } 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 cb6707785d9..12618d40780 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 @@ -10,7 +10,13 @@ import java.util.LinkedHashSet; import java.util.Set; +/** The type factory for forbidding use of the DisbarUse type. */ public class DisbarUseTypeFactory extends BaseAnnotatedTypeFactory { + /** + * Creates a new DisbarUseTypeFactory. + * + * @param checker the checker + */ public DisbarUseTypeFactory(BaseTypeChecker checker) { super(checker); postInit(); diff --git a/checker/tests/index/Issue2029.java b/checker/tests/index/Issue2029.java index eed83f125dd..599605cbd07 100644 --- a/checker/tests/index/Issue2029.java +++ b/checker/tests/index/Issue2029.java @@ -23,6 +23,7 @@ void LessThanOffsetUpperBound( @NonNegative @LessThan("#3 + 1") int index) { @NonNegative int m = n - k; int[] arr = new int[size]; + // :: error: (unary.increment) for (; index < arr.length - 1; index++) { arr[index] = 10; } diff --git a/checker/tests/index/LessThanBug.java b/checker/tests/index/LessThanBug.java new file mode 100644 index 00000000000..56cdffc9488 --- /dev/null +++ b/checker/tests/index/LessThanBug.java @@ -0,0 +1,15 @@ +import org.checkerframework.checker.index.qual.LessThan; +import org.checkerframework.common.value.qual.IntRange; +import org.checkerframework.common.value.qual.IntVal; + +public class LessThanBug { + + void call() { + bug(30, 1); + } + + void bug(@IntRange(to = 42) int a, @IntVal(1) int c) { + // :: error: (assignment) + @LessThan("c") int x = a; + } +} diff --git a/checker/tests/index/LessThanValue.java b/checker/tests/index/LessThanValue.java index 3d3a47f17f4..41924cfdbc2 100644 --- a/checker/tests/index/LessThanValue.java +++ b/checker/tests/index/LessThanValue.java @@ -28,6 +28,7 @@ void lub(int x, int y, @LessThan({"#1", "#2"}) int a, @LessThan("#1") int b) { void transitive(int a, int b, int c) { if (a < b) { if (b < c) { + // :: error: (assignment) @LessThan("c") int x = a; } } @@ -115,6 +116,7 @@ void count(int count) { newCapacity = Integer.MAX_VALUE; // guaranteed to be >= newCapacity } + // :: error: (return) return newCapacity; } } diff --git a/checker/tests/nullness/java17/Issue5967.java b/checker/tests/nullness/java17/Issue5967.java new file mode 100644 index 00000000000..e2726504eb7 --- /dev/null +++ b/checker/tests/nullness/java17/Issue5967.java @@ -0,0 +1,24 @@ +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.function.Supplier; + +// @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; + } +} diff --git a/checker/tests/nullness/java17/SwitchTestIssue5412.java b/checker/tests/nullness/java17/SwitchTestIssue5412.java index 87d713f5cc7..0f975b6a341 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.type.incompatible) 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..e103357346f --- /dev/null +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -0,0 +1,98 @@ +// @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 + +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) { + 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; + } + msg.toString(); + } + + 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/checker/tests/nullness/java21/SimpleCaseGuard.java b/checker/tests/nullness/java21/SimpleCaseGuard.java new file mode 100644 index 00000000000..805c93d5b91 --- /dev/null +++ b/checker/tests/nullness/java21/SimpleCaseGuard.java @@ -0,0 +1,31 @@ +// @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 + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +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/checker/tests/resourceleak/MultipleOwnedResources.java b/checker/tests/resourceleak/MultipleOwnedResources.java new file mode 100644 index 00000000000..5ae8e56825b --- /dev/null +++ b/checker/tests/resourceleak/MultipleOwnedResources.java @@ -0,0 +1,29 @@ +// Test case for https://github.com/typetools/checker-framework/issues/5911 + +import org.checkerframework.checker.calledmethods.qual.*; +import org.checkerframework.checker.mustcall.qual.*; + +import java.io.*; + +class MultipleOwnedResources implements Closeable { + + private final @Owning Closeable r1; + private final @Owning Closeable r2; + + public MultipleOwnedResources(@Owning Closeable r1, @Owning Closeable r2) { + this.r1 = r1; + this.r2 = r2; + } + + @Override + @EnsuresCalledMethods( + value = {"r1", "r2"}, + methods = {"close"}) + public void close() throws IOException { + try { + r1.close(); + } finally { + r2.close(); + } + } +} diff --git a/checker/tests/resourceleak/MultipleOwnedResourcesOfDifferentTypes.java b/checker/tests/resourceleak/MultipleOwnedResourcesOfDifferentTypes.java new file mode 100644 index 00000000000..1478d1f4676 --- /dev/null +++ b/checker/tests/resourceleak/MultipleOwnedResourcesOfDifferentTypes.java @@ -0,0 +1,44 @@ +// Test case for https://github.com/typetools/checker-framework/issues/5911 + +import org.checkerframework.checker.calledmethods.qual.*; +import org.checkerframework.checker.mustcall.qual.*; + +class MultipleOwnedResourcesOfDifferentTypes { + + @InheritableMustCall("a") + static class Foo { + void a() {} + } + + @InheritableMustCall("b") + static class Bar { + void b() {} + } + + @InheritableMustCall("finalizer") + static class OwningField { + + private final @Owning Foo owningFoo; + + private final @Owning Bar owningBar; + + public OwningField() { + this.owningFoo = new Foo(); + this.owningBar = new Bar(); + } + + @EnsuresCalledMethods( + value = {"owningBar"}, + methods = {"b"}) + @EnsuresCalledMethods( + value = {"this.owningFoo"}, + methods = {"a"}) + void finalizer() { + try { + this.owningFoo.a(); + } finally { + this.owningBar.b(); + } + } + } +} 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". 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 diff --git a/dataflow/build.gradle b/dataflow/build.gradle index b9ad55738ea..abf136a82b9 100644 --- a/dataflow/build.gradle +++ b/dataflow/build.gradle @@ -45,7 +45,8 @@ def createDataflowShaded(shadedPkgName) { } // Relocate external dependencies - relocate 'org.plumelib', "org.checkerframework.${shadedPkgName}.org.plumelib" + relocate 'org.plume', "org.checkerframework.${shadedPkgName}.org.plume" + relocate 'com.google', 'org.checkerframework.com.google' } } 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 fb45a5d10ca..d7471d0f925 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; @@ -80,6 +81,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; @@ -142,6 +144,12 @@ 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; @@ -558,6 +566,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 } @@ -577,7 +587,7 @@ public Node scan(Tree tree, Void p) { * @return the result of visiting the switch expression tree */ public Node visitYield17(Tree yieldTree, Void p) { - ExpressionTree resultExpression = TreeUtils.yieldTreeGetValue(yieldTree); + ExpressionTree resultExpression = YieldUtils.getValue(yieldTree); switchBuilder.buildSwitchExpressionResult(resultExpression); return null; } @@ -608,7 +618,7 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { ClassTree enclosingClass = TreePathUtil.enclosingClass(getCurrentPath()); TypeElement classElem = TreeUtils.elementFromDeclaration(enclosingClass); Node receiver = new ImplicitThisNode(classElem.asType()); - VariableTree varTree = TreeUtils.bindingPatternTreeGetVariable(bindingPatternTree); + VariableTree varTree = BindingPatternUtils.getVariable(bindingPatternTree); VariableDeclarationNode variableDeclarationNode = new VariableDeclarationNode(varTree); extendWithNode(variableDeclarationNode); LocalVariableNode varNode = new LocalVariableNode(varTree, receiver); @@ -616,6 +626,28 @@ public Node visitBindingPattern17(Tree bindingPatternTree, Void p) { return varNode; } + /** + * Visit a DeconstructionPatternTree. + * + * @param deconstructionPatternTree a DeconstructionPatternTree, typed as Tree so the Checker + * Framework compiles under JDK 20 and earlier + * @param p an unused parameter + * @return the result of visiting the tree + */ + public Node visitDeconstructionPattern21(Tree deconstructionPatternTree, Void p) { + List nestedPatternTrees = + DeconstructionPatternUtils.getNestedPatterns(deconstructionPatternTree); + List nestedPatterns = new ArrayList<>(nestedPatternTrees.size()); + for (Tree pattern : nestedPatternTrees) { + nestedPatterns.add(scan(pattern, p)); + } + + return new DeconstructorPatternNode( + TreeUtils.typeOf(deconstructionPatternTree), + deconstructionPatternTree, + nestedPatterns); + } + /* --------------------------------------------------------- */ /* Nodes and Labels Management */ /* --------------------------------------------------------- */ @@ -2376,8 +2408,8 @@ private SwitchBuilder(Tree switchTree) { this.caseTrees = switchStatementTree.getCases(); this.selectorExprTree = switchStatementTree.getExpression(); } else { - this.caseTrees = TreeUtils.switchExpressionTreeGetCases(switchTree); - this.selectorExprTree = TreeUtils.switchExpressionTreeGetExpression(switchTree); + this.caseTrees = SwitchExpressionUtils.getCases(switchTree); + this.selectorExprTree = SwitchExpressionUtils.getExpression(switchTree); } // "+ 1" for the default case. If the switch has an explicit default case, then // the last element of the array is never used. @@ -2422,16 +2454,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 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 && casesAreExhaustive() && noFallthroughToHere; - buildCase(caseTree, i, isLastOfExhaustive); + buildCase(caseTree, i, false); } } @@ -2534,19 +2561,19 @@ private void buildSwitchExpressionVar() { /** * Build the CFG for the given case tree. * - * @param tree a case tree whose CFG to build + * @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 + * @param isLastCaseOfExhaustive 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 isLastCaseOfExhaustive) { + boolean isDefaultCase = TreeUtils.isDefaultCaseTree(caseTree); // If true, no test of labels is necessary. - // Unfortunately, if isLastOfExhaustive==TRUE, no flow-sensitive refinement occurs + // Unfortunately, if isLastCaseOfExhaustive==TRUE, no flow-sensitive refinement occurs // within the body of the CaseNode. In the future, that can be performed, but it // requires addition of InfeasibleExitBlock, a new SpecialBlock in the CFG. - boolean isTerminalCase = isDefaultCase || isLastOfExhaustive; + boolean isTerminalCase = isDefaultCase || isLastCaseOfExhaustive; Label thisBodyLabel = caseBodyLabels[index]; Label nextBodyLabel = caseBodyLabels[index + 1]; @@ -2557,22 +2584,27 @@ 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 (Tree exprTree : CaseUtils.getLabels(caseTree)) { exprs.add(scan(exprTree, null)); } + + ExpressionTree guardTree = CaseUtils.getGuard(caseTree); + Node guard = (guardTree == null) ? null : scan(guardTree, null); + CaseNode test = - new CaseNode(tree, selectorExprAssignment, exprs, env.getTypeUtils()); + new CaseNode( + caseTree, selectorExprAssignment, exprs, guard, 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. @@ -2583,7 +2615,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 = CaseUtils.getBody(caseTree); if (!TreeUtils.isSwitchStatement(switchTree) && bodyTree instanceof ExpressionTree) { buildSwitchExpressionResult((ExpressionTree) bodyTree); @@ -2639,40 +2671,47 @@ private void buildCase(CaseTree tree, int index, boolean isLastOfExhaustive) { } /** - * 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. + * Returns true if the switch is exhaustive; ignoring any default case * - * @return true if the cases are exhaustive + * @return true if the switch is exhaustive; ignoring any default case */ - private boolean casesAreExhaustive() { - TypeMirror selectorTypeMirror = TreeUtils.typeOf(selectorExprTree); + 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)) { + return true; + } - 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)) { - caseLabels.add(((IdentifierTree) caseEnumConstant).getName()); - } - } - // Could also check that the values match. - boolean result = enumConstants.size() == caseLabels.size(); - return result; + int enumCaseLabels = 0; + for (CaseTree caseTree : caseTrees) { + for (Tree caseLabel : CaseUtils.getLabels(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)) { + 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) { + // The switch expression's type is an enumerated type. + List enumConstants = + ElementUtils.getEnumConstants(declaredTypeElement); + return enumConstants.size() == enumCaseLabels; + } + } + return false; } } @@ -3935,15 +3974,18 @@ public Node visitTypeParameter(TypeParameterTree tree, Void p) { @Override public Node visitInstanceOf(InstanceOfTree tree, Void p) { + InstanceOfNode instanceOfNode; 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)); - - InstanceOfNode node = new InstanceOfNode(tree, operand, bindingNode, refType, types); - extendWithNode(node); - return node; + Tree patternTree = InstanceOfUtils.getPattern(tree); + if (patternTree != null) { + Node pattern = scan(patternTree, p); + instanceOfNode = new InstanceOfNode(tree, operand, pattern, pattern.getType(), types); + } else { + TypeMirror refType = TreeUtils.typeOf(tree.getType()); + instanceOfNode = new InstanceOfNode(tree, operand, refType, types); + } + 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 f6b82e8a9d3..4f04678f1a4 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/CaseNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/CaseNode.java index 81187c6efcd..540d37684ec 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 @@ -38,6 +38,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,17 +48,20 @@ 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( 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; } /** @@ -78,6 +84,15 @@ public List getCaseOperands() { return caseExprs; } + /** + * Gets the node for the guard (the expression in the {@code when} clause). + * + * @return the node for the guard + */ + public @Nullable Node getGuard() { + return guard; + } + @Override public CaseTree getTree() { return tree; 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..ae92ece125d --- /dev/null +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/DeconstructorPatternNode.java @@ -0,0 +1,98 @@ +package org.checkerframework.dataflow.cfg.node; + +import com.sun.source.tree.Tree; + +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.lang.model.type.TypeMirror; + +/** A node for a deconstrutor pattern. */ +public class DeconstructorPatternNode extends Node { + /** + * The {@code DeconstructorPatternTree}, declared as {@link Tree} to permit this file to compile + * under JDK 20 and earlier. + */ + private final Tree deconstructorPattern; + + /** A list of nested pattern nodes. */ + private final List nestedPatterns; + + /** + * Creates a {@code DeconstructorPatternNode}. + * + * @param type the type of the node + * @param deconstructorPattern the {@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 + @Pure + public @Nullable Tree getTree() { + return deconstructorPattern; + } + + /** + * Returns the nested patterns. + * + * @return the nested patterns + */ + @Pure + public List getNestedPatterns() { + return nestedPatterns; + } + + @Override + public R accept(NodeVisitor visitor, P p) { + return visitor.visitDeconstructorPattern(this, p); + } + + @Override + @Pure + public Collection getOperands() { + return nestedPatterns; + } + + /** + * A list of nested binding variables. This is lazily initialized and should only be accessed by + * {@link #getBindingVariables()}. + */ + private @MonotonicNonNull List bindingVariables = null; + + /** + * Return all the binding variables in this pattern. + * + * @return all the binding variables in this pattern + */ + public List getBindingVariables() { + if (bindingVariables == null) { + if (nestedPatterns.isEmpty()) { + bindingVariables = Collections.emptyList(); + } else { + bindingVariables = new ArrayList<>(nestedPatterns.size()); + for (Node patternNode : nestedPatterns) { + if (patternNode instanceof LocalVariableNode) { + bindingVariables.add((LocalVariableNode) patternNode); + } else { + bindingVariables.addAll( + ((DeconstructorPatternNode) patternNode).getBindingVariables()); + } + } + bindingVariables = Collections.unmodifiableList(bindingVariables); + } + } + 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 8336e5f7e32..4d30bdfe9f3 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 @@ -2,12 +2,14 @@ import com.sun.source.tree.InstanceOfTree; +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; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Objects; import javax.lang.model.type.TypeKind; @@ -30,8 +32,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 if one exists. */ + protected final @Nullable Node patternNode; /** For Types.isSameType. */ protected final Types types; @@ -53,14 +55,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)); @@ -68,7 +70,7 @@ public InstanceOfNode( this.operand = operand; this.refType = refType; this.types = types; - this.bindingVariable = bindingVariable; + this.patternNode = patternNode; } public Node getOperand() { @@ -79,9 +81,47 @@ 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 // 2023-09-24 public @Nullable LocalVariableNode getBindingVariable() { - return bindingVariable; + if (patternNode instanceof LocalVariableNode) { + return (LocalVariableNode) patternNode; + } + return null; + } + + /** + * A list of all binding variables in this instanceof. This is lazily initialized, use {@link + * #getBindingVariables()}. + */ + @MonotonicNonNull List bindingVariables = null; + + /** + * Return all the binding variables in this instanceof. + * + * @return all the binding variables in this instanceof + */ + public List getBindingVariables() { + if (bindingVariables == null) { + if (patternNode instanceof DeconstructorPatternNode) { + bindingVariables = ((DeconstructorPatternNode) patternNode).getBindingVariables(); + } else if (patternNode instanceof LocalVariableNode) { + bindingVariables = Collections.singletonList((LocalVariableNode) patternNode); + } else { + bindingVariables = Collections.emptyList(); + } + } + 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; } /** @@ -109,7 +149,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 84b0327cbf9..20211a9619b 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/dataflow/src/main/java/org/checkerframework/dataflow/cfg/visualize/CFGVisualizeLauncher.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/visualize/CFGVisualizeLauncher.java index 3c5be6f8e85..4d8a6a97abd 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/visualize/CFGVisualizeLauncher.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/visualize/CFGVisualizeLauncher.java @@ -230,10 +230,11 @@ private static ControlFlowGraph generateMethodCFG(String file, String clas, Stri JavaCompiler javac = new JavaCompiler(context); JavaFileObject l; - // The @MustCall annotation is required to avoid a warning about incompatible generic types - try (JavacFileManager fileManager = - (JavacFileManager) - context.<@MustCall({}) JavaFileManager>get(JavaFileManager.class)) { + try (@SuppressWarnings( + "mustcall:type.argument") // Context isn't annotated for the Must Call + // Checker. + JavacFileManager fileManager = + (JavacFileManager) context.get(JavaFileManager.class)) { l = fileManager.getJavaFileObjectsFromStrings(List.of(file)).iterator().next(); } catch (IOException e) { throw new Error(e); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 34a7e9ba80a..e7107c1a1e2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,4 +1,4 @@ -Version 3.38.0-eisop1 (October ?, 2023) +Version 3.39.0-eisop1 (October ?, 2023) --------------------------------------- **User-visible changes:** @@ -48,6 +48,30 @@ Changed the return types of eisop#297, eisop#376, eisop#400, eisop#519, eisop#532, eisop#533, typetools#1590, typetools#1919. +Version 3.39.0 (October 2, 2023) +-------------------------------- + +**User-visible changes:** + +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` must be updated. + * Method `InstanceOfNode.getBindingVariable()` is deprecated; use + `getPatternNode()` or `getBindingVariables()` instead. + +WPI uses 1-based indexing for formal parameters and arguments. + +**Closed issues:** + +#5911, #5967, #6155, #6173, #6201. + + Version 3.38.0 (September 1, 2023) ---------------------------------- diff --git a/docs/checker-framework-webpage.html b/docs/checker-framework-webpage.html index af9e37e1265..29fff43c8d7 100644 --- a/docs/checker-framework-webpage.html +++ b/docs/checker-framework-webpage.html @@ -30,8 +30,8 @@

The Checker Framework

Installation instructions and tutorial.
  • - Download: checker-framework-3.38.0.zip - (1 Sep 2023); + Download: checker-framework-3.39.0.zip + (2 Oct 2023); includes source, platform-independent binary, tests, and documentation.
    Then, see the installation @@ -93,7 +93,7 @@

    The Checker Framework

    the .class file. The tools support both Java 5 declaration annotations and Java 8 type annotations.