-
Notifications
You must be signed in to change notification settings - Fork 35
Support for class Constants #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
CatarinaGamboa
wants to merge
6
commits into
main
Choose a base branch
from
finalstatics
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
13693c5
get value of final static vars
CatarinaGamboa 7e17ba4
Resolve static final constants inside refinement predicates
CatarinaGamboa 3a5d2fe
change class names
CatarinaGamboa 9cacea7
forgot to save
CatarinaGamboa 2e77dfa
add more javadoc
CatarinaGamboa faaeb06
keep translation from final to literal value
CatarinaGamboa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| LiquidJava is an additional type checker for Java that adds **liquid types** (refinements) and **typestates** on top of standard Java. Users annotate Java code with `@Refinement`, `@StateRefinement`, `@StateSet` etc. (from `liquidjava-api`); the verifier parses the program with [Spoon](https://spoon.gforge.inria.fr/), translates refinement predicates to SMT, and discharges verification conditions with **Z3**. | ||
|
|
||
| Requires **Java 20+** and **Maven 3.6+** (the parent POM declares 1.8 source/target, but the verifier module overrides to 20). | ||
|
|
||
| ## Module Layout | ||
|
|
||
| This is a Maven multi-module build (`pom.xml` is the umbrella): | ||
|
|
||
| - `liquidjava-api` — published annotations (`@Refinement`, `@RefinementAlias`, `@StateRefinement`, `@StateSet`, ghost functions). Stable artifact users depend on. | ||
| - `liquidjava-verifier` — the actual checker (Spoon processor + RJ AST + SMT translator). Published as `io.github.liquid-java:liquidjava-verifier`. | ||
| - `liquidjava-example` — sample programs **and the test suite** under `src/main/java/testSuite/`. The verifier's tests scan this directory. | ||
|
|
||
| Verifier package map (`liquidjava-verifier/src/main/java/liquidjava/`): | ||
| - `api/` — entrypoints; `CommandLineLauncher` is the CLI main. | ||
| - `processor/` — Spoon processors. `RefinementProcessor` orchestrates; `refinement_checker/` contains `RefinementTypeChecker`, `MethodsFirstChecker`, `ExternalRefinementTypeChecker`, plus `general_checkers/` and `object_checkers/` for typestate. | ||
| - `ast/` — AST of the Refinements Language (RJ). | ||
| - `rj_language/` — parser from refinement strings to RJ AST. | ||
| - `smt/` — Z3 translation (`TranslatorToZ3`, `ExpressionToZ3Visitor`, `SMTEvaluator`, `Counterexample`). | ||
| - `errors/`, `utils/`, `diagnostics/`. | ||
|
|
||
| ## Commands | ||
|
|
||
| Build / install everything: | ||
| ```bash | ||
| mvn clean install | ||
| ``` | ||
|
|
||
| Run the test suite (verifier module, runs whole `testSuite/` dir): | ||
| ```bash | ||
| mvn test | ||
| ``` | ||
|
|
||
| Run a single test method (JUnit 4/5 mix — both work via Surefire): | ||
| ```bash | ||
| mvn -pl liquidjava-verifier -Dtest=TestExamples test | ||
| mvn -pl liquidjava-verifier -Dtest=TestExamples#testMultiplePaths test | ||
| ``` | ||
|
|
||
| Verify a specific file/directory from CLI (uses the `liquidjava` script in repo root, macOS/Linux): | ||
| ```bash | ||
| ./liquidjava liquidjava-example/src/main/java/testSuite/CorrectSimpleAssignment.java | ||
| ``` | ||
| Equivalent raw form: | ||
| ```bash | ||
| mvn exec:java -pl liquidjava-verifier \ | ||
| -Dexec.mainClass="liquidjava.api.CommandLineLauncher" \ | ||
| -Dexec.args="/path/to/file_or_dir" | ||
| ``` | ||
|
|
||
| Code formatting runs automatically in the `validate` phase via `formatter-maven-plugin` (configured for Java 20 in `liquidjava-verifier/pom.xml`); no separate lint command. | ||
|
|
||
| ## Test Suite Conventions | ||
|
|
||
| Tests are discovered by `TestExamples#testPath` (parameterized) under `liquidjava-example/src/main/java/testSuite/`: | ||
|
|
||
| - Single-file cases: filename starts with `Correct…` or `Error…`. | ||
| - Directory cases: directory name contains the substring `correct` or `error`. | ||
| - Anything else is **ignored** (so helper sources can live alongside). | ||
| - Expected error for a failing case: | ||
| - Single file: write the expected error title in a comment on the **first line** of the file. | ||
| - Directory: place a `.expected` file in that directory containing the expected error title. | ||
|
|
||
| When adding new test cases, place them under `liquidjava-example/src/main/java/testSuite/` following the naming rules above — that is the only way they get picked up. | ||
|
|
||
| ## Architecture Notes That Span Files | ||
|
|
||
| - **Two-pass typechecking.** `MethodsFirstChecker` collects method signatures and refinement contracts before `RefinementTypeChecker` walks bodies, so forward references and recursion resolve. Edits to one usually need a matching change in the other. | ||
| - **Refinement string → AST → Z3.** A `@Refinement("a > 0")` string flows: `rj_language` parser → `ast` nodes → `smt/TranslatorToZ3` / `ExpressionToZ3Visitor`. New predicate forms generally require touching all three. | ||
| - **External refinements.** `ExternalRefinementTypeChecker` plus `*Refinements.java` companion files specify contracts for third-party APIs without modifying their sources. The `co-specifying-liquidjava` skill covers this workflow. | ||
| - **Typestate** lives in `processor/refinement_checker/object_checkers/` and uses `@StateRefinement` / `@StateSet` from the API. Ghost-state predicates flow through the same SMT pipeline as value refinements. | ||
| - **Z3 dependency.** The verifier shells out to Z3 via JNI bindings; failures often surface as `SMTResult` errors or counterexamples, not Java exceptions. |
25 changes: 25 additions & 0 deletions
25
liquidjava-example/src/main/java/testSuite/CorrectImageWriteParamModes.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package testSuite; | ||
|
|
||
| import javax.imageio.ImageWriteParam; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class CorrectImageWriteParamModes { | ||
|
|
||
| static void requireExplicit( | ||
| @Refinement("_ == ImageWriteParam.MODE_EXPLICIT") int mode) { | ||
| } | ||
|
|
||
| static void requireKnownMode( | ||
| @Refinement("_ == ImageWriteParam.MODE_DISABLED || _ == ImageWriteParam.MODE_DEFAULT " | ||
| + "|| _ == ImageWriteParam.MODE_EXPLICIT || _ == ImageWriteParam.MODE_COPY_FROM_METADATA") int mode) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| requireExplicit(ImageWriteParam.MODE_EXPLICIT); | ||
| requireKnownMode(ImageWriteParam.MODE_DEFAULT); | ||
| requireKnownMode(ImageWriteParam.MODE_DISABLED); | ||
| requireKnownMode(2); | ||
| } | ||
| } |
27 changes: 27 additions & 0 deletions
27
liquidjava-example/src/main/java/testSuite/CorrectStaticFinalConstant.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package testSuite; | ||
|
|
||
| import javax.imageio.ImageWriteParam; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class CorrectStaticFinalConstant { | ||
|
|
||
| static void requirePositive(@Refinement("_ > 0") int x) { | ||
| } | ||
|
|
||
| static void requireAtLeast(@Refinement("_ >= 1024") int x) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| // Reflective resolution of a JDK static final int constant. | ||
| requirePositive(Integer.MAX_VALUE); | ||
|
|
||
| // Reflective resolution of a JDK static final int with a known concrete value. | ||
| requireAtLeast(Short.MAX_VALUE); | ||
| } | ||
|
|
||
| void other(){ | ||
| @Refinement("_ > 0 && _ <= 1") int x = ImageWriteParam.MODE_DEFAULT; | ||
| } | ||
| } |
25 changes: 25 additions & 0 deletions
25
liquidjava-example/src/main/java/testSuite/CorrectStaticFinalInPredicate.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class CorrectStaticFinalInPredicate { | ||
|
|
||
| // Constants resolved inside the predicate string itself. | ||
| static void clampInt(@Refinement("_ >= Integer.MIN_VALUE && _ <= Integer.MAX_VALUE") int x) { | ||
| } | ||
|
|
||
| static void belowMaxLong(@Refinement("_ <= Long.MAX_VALUE") long x) { | ||
| } | ||
|
|
||
| static void notMaxByte(@Refinement("_ != Byte.MAX_VALUE") int x) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| clampInt(0); | ||
| clampInt(Integer.MAX_VALUE); | ||
| clampInt(Integer.MIN_VALUE); | ||
| belowMaxLong(123L); | ||
| notMaxByte(0); | ||
| } | ||
| } |
26 changes: 26 additions & 0 deletions
26
liquidjava-example/src/main/java/testSuite/CorrectStaticImportInPredicate.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package testSuite; | ||
|
|
||
| import static java.lang.Math.PI; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| /** | ||
| * Locks in that the import walk ignores {@code import static} (kind {@code FIELD}) without confusing the resolver: | ||
| * the file has a static import for an unrelated symbol, and a refinement that uses a regular {@code Type.CONST} | ||
| * reference. The verifier must skip the static import and resolve {@code Math.PI} via the implicit {@code java.lang} | ||
| * fallback (or the static-import target's class — either path is correct here). | ||
| */ | ||
| @SuppressWarnings("unused") | ||
| public class CorrectStaticImportInPredicate { | ||
|
|
||
| static double useUnused() { | ||
| return PI; | ||
| } | ||
|
|
||
| static void requireBelowFour(@Refinement("_ < 4.0") double x) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| requireBelowFour(Math.PI); | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
liquidjava-example/src/main/java/testSuite/ErrorImageWriteParamModes.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package testSuite; | ||
|
|
||
| import javax.imageio.ImageWriteParam; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class ErrorImageWriteParamModes { | ||
|
|
||
| static void requireExplicit(@Refinement("_ == ImageWriteParam.MODE_EXPLICIT") int mode) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| // MODE_DEFAULT is 1, not 2 (MODE_EXPLICIT). | ||
| requireExplicit(ImageWriteParam.MODE_DEFAULT); // Refinement Error | ||
| } | ||
| } |
14 changes: 14 additions & 0 deletions
14
liquidjava-example/src/main/java/testSuite/ErrorStaticFinalConstant.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class ErrorStaticFinalConstant { | ||
|
|
||
| static void requirePositive(@Refinement("_ > 0") int x) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| requirePositive(Integer.MIN_VALUE); // Refinement Error | ||
| } | ||
| } |
15 changes: 15 additions & 0 deletions
15
liquidjava-example/src/main/java/testSuite/ErrorStaticFinalInPredicate.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class ErrorStaticFinalInPredicate { | ||
|
|
||
| static void belowMaxByte(@Refinement("_ <= Byte.MAX_VALUE") int x) { | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| // Byte.MAX_VALUE == 127, so 200 violates the bound. | ||
| belowMaxByte(200); // Refinement Error | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
...va-example/src/main/java/testSuite/classes/missing_import_final_error/ClassImporting.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package testSuite.classes.missing_import_final_error; | ||
|
|
||
| import javax.imageio.ImageWriteParam; | ||
|
|
||
| /** Sibling file that imports the class so the verifier knows where to find it. */ | ||
| public class ClassImporting { | ||
| public static int explicit() { | ||
| return ImageWriteParam.MODE_EXPLICIT; | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
...ava-example/src/main/java/testSuite/classes/missing_import_final_error/ClassNoImport.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package testSuite.classes.missing_import_final_error; | ||
|
|
||
| import liquidjava.specification.Refinement; | ||
|
|
||
| @SuppressWarnings("unused") | ||
| public class ClassNoImport { | ||
|
|
||
| // No import for javax.imageio.ImageWriteParam in this file — the verifier | ||
| // should suggest it because Helper.java already imports it. | ||
| static void requireExplicit(@Refinement("_ == ImageWriteParam.MODE_EXPLICIT") int mode) { // Error | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| requireExplicit(2); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it will just see if there are enums to resolve in a small expression, if there are no enums it doesnt do anything else, so its fine