|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +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**. |
| 8 | + |
| 9 | +Requires **Java 20+** and **Maven 3.6+** (the parent POM declares 1.8 source/target, but the verifier module overrides to 20). |
| 10 | + |
| 11 | +## Module Layout |
| 12 | + |
| 13 | +This is a Maven multi-module build (`pom.xml` is the umbrella): |
| 14 | + |
| 15 | +- `liquidjava-api` — published annotations (`@Refinement`, `@RefinementAlias`, `@StateRefinement`, `@StateSet`, ghost functions). Stable artifact users depend on. |
| 16 | +- `liquidjava-verifier` — the actual checker (Spoon processor + RJ AST + SMT translator). Published as `io.github.liquid-java:liquidjava-verifier`. |
| 17 | +- `liquidjava-example` — sample programs **and the test suite** under `src/main/java/testSuite/`. The verifier's tests scan this directory. |
| 18 | + |
| 19 | +Verifier package map (`liquidjava-verifier/src/main/java/liquidjava/`): |
| 20 | +- `api/` — entrypoints; `CommandLineLauncher` is the CLI main. |
| 21 | +- `processor/` — Spoon processors. `RefinementProcessor` orchestrates; `refinement_checker/` contains `RefinementTypeChecker`, `MethodsFirstChecker`, `ExternalRefinementTypeChecker`, plus `general_checkers/` and `object_checkers/` for typestate. |
| 22 | +- `ast/` — AST of the Refinements Language (RJ). |
| 23 | +- `rj_language/` — parser from refinement strings to RJ AST. |
| 24 | +- `smt/` — Z3 translation (`TranslatorToZ3`, `ExpressionToZ3Visitor`, `SMTEvaluator`, `Counterexample`). |
| 25 | +- `errors/`, `utils/`, `diagnostics/`. |
| 26 | + |
| 27 | +## Commands |
| 28 | + |
| 29 | +Build / install everything: |
| 30 | +```bash |
| 31 | +mvn clean install |
| 32 | +``` |
| 33 | + |
| 34 | +Run the test suite (verifier module, runs whole `testSuite/` dir): |
| 35 | +```bash |
| 36 | +mvn test |
| 37 | +``` |
| 38 | + |
| 39 | +Run a single test method (JUnit 4/5 mix — both work via Surefire): |
| 40 | +```bash |
| 41 | +mvn -pl liquidjava-verifier -Dtest=TestExamples test |
| 42 | +mvn -pl liquidjava-verifier -Dtest=TestExamples#testMultiplePaths test |
| 43 | +``` |
| 44 | + |
| 45 | +Verify a specific file/directory from CLI (uses the `liquidjava` script in repo root, macOS/Linux): |
| 46 | +```bash |
| 47 | +./liquidjava liquidjava-example/src/main/java/testSuite/CorrectSimpleAssignment.java |
| 48 | +``` |
| 49 | +Equivalent raw form: |
| 50 | +```bash |
| 51 | +mvn exec:java -pl liquidjava-verifier \ |
| 52 | + -Dexec.mainClass="liquidjava.api.CommandLineLauncher" \ |
| 53 | + -Dexec.args="/path/to/file_or_dir" |
| 54 | +``` |
| 55 | + |
| 56 | +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. |
| 57 | + |
| 58 | +## Test Suite Conventions |
| 59 | + |
| 60 | +Tests are discovered by `TestExamples#testPath` (parameterized) under `liquidjava-example/src/main/java/testSuite/`: |
| 61 | + |
| 62 | +- Single-file cases: filename starts with `Correct…` or `Error…`. |
| 63 | +- Directory cases: directory name contains the substring `correct` or `error`. |
| 64 | +- Anything else is **ignored** (so helper sources can live alongside). |
| 65 | +- Expected error for a failing case: |
| 66 | + - Single file: write the expected error title in a comment on the **first line** of the file. |
| 67 | + - Directory: place a `.expected` file in that directory containing the expected error title. |
| 68 | + |
| 69 | +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. |
| 70 | + |
| 71 | +## Architecture Notes That Span Files |
| 72 | + |
| 73 | +- **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. |
| 74 | +- **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. |
| 75 | +- **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. |
| 76 | +- **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. |
| 77 | +- **Z3 dependency.** The verifier shells out to Z3 via JNI bindings; failures often surface as `SMTResult` errors or counterexamples, not Java exceptions. |
0 commit comments