Repository navigation
Releases: mihaidaniel34/grounds
Release list
grounds 0.0.11
Release notes follow.
grounds 0.0.10
What's Changed
- DI: @SpringBootApplication's scan skips auto-configurations by @mihaidaniel34 in #23
- Release 0.0.10 by @mihaidaniel34 in #24
Full Changelog: v0.0.9...v0.0.10
grounds 0.0.9
Type inference you can trust, more breaks caught across files, and Spring libraries. Still experimental. Binaries for macOS (Apple Silicon, Intel) and Linux (x86_64, arm64, statically linked: any distribution, Alpine and NixOS included).
target=aarch64-apple-darwin # x86_64-apple-darwin, x86_64-unknown-linux-musl, aarch64-unknown-linux-musl
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.9/grounds-0.0.9-$target.tar.gz | tar -xz
sudo mv grounds-0.0.9-$target/grounds /usr/local/bin/Each archive has a .sha256. The macOS binaries are not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
Overload resolution and inference, checked against javac
Rules trust the types grounds is certain of, so a confidently wrong type is worse than an unknown one. Three differential fuzzers now compare grounds with javac on every pull request and nightly, and the inference was rebuilt until they agreed:
- explicit type arguments, inferred bounds and F-bounds, wildcard capture, unchecked conversion (which erases the result), and ambiguous
nullcalls; - lambda inputs resolved before the body, explicitly typed lambdas, method references treated as inexact, and target types bounding results from above;
- generic calls and diamonds used as arguments that adapt to their parameter (
collect(Collectors.groupingBy(...)),Map<Number, String> m = s.collect(toMap(...))); - a single remaining candidate is trusted only when no other overload can apply in any phase. An argument of unknown type that might be raw makes only the result's erasure certain.
In numbers:
- the first fuzz run found 95 confidently wrong call types; now there are none, over 16,000 generated calls;
- calls javac rejects but grounds typed with certainty fell from 2,187 to a handful;
- an argument of unknown type (no jar for it) no longer hides what the other arguments prove:
new String(Objects.requireNonNullElse(x, new byte[0]))is stillString(byte[]).
CI mode: more breaks across files
grounds changed --base now also reports, in unchanged files:
- uses of a deleted or renamed class (imported, by its simple name, or fully qualified);
- calls and method references to a method made private (including implicit calls inherited from a superclass);
- static imports of a member that no longer exists;
- a changed return type assigned to an incompatible variable (
incompatible-assignment, new); - a new bean that makes an injection ambiguous.
Each one stays silent wherever another class could legally take the name: another import, a wildcard package grounds cannot see, an inherited member, or a class defined in two modules.
Spring libraries
A project is an application only if it has a @SpringBootApplication / @SpringBootConfiguration class, or a @ComponentScan class with main. Otherwise it's a library: injections in its main code are left to the consuming application, so there are no bean-not-found reports for beans a consumer provides, and no bean-name clashes. A bean that injects its own type to wrap another implementation (a decorator) is no longer reported as ambiguous; grounds skips self-references the way Spring does (#15).
grounds classpath
Multi-module Maven projects that were never installed (most fresh clones) now get a classpath: grounds retries through the lifecycle up to test-compile, with compilation and checkers skipped, and installs nothing into ~/.m2.
Fixes
- Without a JDK, grounds warns once and stays silent, instead of reporting
toString()andgetClass()as missing (225 false errors on camunda). unresolved-method: an array passed alone in a varargs position counts as one element (of(byte[])forof(byte[]...)).- DI: Jakarta Data repositories are beans; wiring that involves a class defined in several source files is left unknown;
ObjectProvider/Providerwith several beans or none is not a startup failure. - Unused-private-method, redundant-cast and infinite-recursion false positives that came from overload picks fixed by the new inference.
Testing
- The corpus now runs in two setups: 26 projects without jars, and 20 with their exact build classpath. It also checks CI mode under scripted edits: 208 safe edits got no reports.
- Before release, Astra reviewed all product code since 0.0.8 in five rounds. That found 13 false positives and 3 missed breaks, all fixed with regression tests.
grounds 0.0.8
CI mode and Linux release. Still experimental. Binaries for macOS (Apple Silicon, Intel) and Linux (x86_64, arm64, statically linked: any distribution, Alpine and NixOS included).
target=aarch64-apple-darwin # x86_64-apple-darwin, x86_64-unknown-linux-musl, aarch64-unknown-linux-musl
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.8/grounds-0.0.8-$target.tar.gz | tar -xz
sudo mv grounds-0.0.8-$target/grounds /usr/local/bin/Each archive has a .sha256. The macOS binaries are not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
CI mode: grounds changed --base <ref>
Checks what a branch or pull request changed since it forked from <ref>, committed or not:
- findings on the changed lines only, so existing code adds no noise;
- places in unchanged files the change broke: calls to removed or renamed members, implementations of a changed interface,
@Overrides of a changed supertype, injection points whose bean was deleted or became ambiguous. grounds analyzes the other files against both versions of the changed ones and keeps what is new (about 1 s on spring-petclinic-rest, 17 s on camunda's 15,700 files); - a
breaks-other-filesfinding on each changed line that broke something, listing the broken places, so the break shows in the diff even though the broken files are not part of it.
--format sarif (also for check) writes SARIF 2.1.0 with paths from the repository root, for GitHub code scanning or reviewdog (use -filter-mode=nofilter, or reviewdog drops the breaks in unchanged files). --exit-zero for jobs that only upload results. The README has a GitHub Actions example.
Tested on 107 pull-request-shaped changes over 8 corpus projects: no false reports. Not yet checked across files: a deleted class, a method made private, static imports, method references, and return types assigned to variables.
Linux
The static Linux binary, a native Nix build and the macOS binary report identical findings, fingerprints included, so baselines carry over between systems. The Nix flake now builds on Linux (its tests no longer depend on jars in the machine's Maven/Gradle caches).
Fixes
- A checkout under a folder named
build, or a package namedbuild, lost those sources: apollo'scom.ctrip.framework.apollo.build.*was never analyzed. - With the project in a subdirectory of the repository, the agent hooks treated every line of a changed file as changed.
- A class defined in several files now resolves to the same one regardless of load order.
unimplemented-abstract-methodquotes the missing methods in its message.
grounds 0.0.7
Call resolution and precision release. Still experimental. The binary is for Apple Silicon, macOS 11+; on other platforms, build from source or use the Nix flake.
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.7/grounds-0.0.7-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.7-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
More calls resolved
AssertJ 3.27's generic assertThat overloads made even assertThat(String) look ambiguous, so nothing chained after it (.isEqualTo(..), ...) was resolved, and rules that need the target stayed silent. An exact overload now beats generic ones, but only when that is provable: anything that would need javac's full type inference stays unknown, as before.
- Calls resolved: spring-ai 76% → 83%, langchain4j 85% → 91%, camunda 83% → 88%, spring-modulith 82% → 87%, rewrite 94% → 98%, spring-petclinic 96% → 99.8%.
- Checked against javac on 8,000 generated overload sets: fewer wrong answers than 0.0.6 (107 → 95, all pre-existing), none new. Five review rounds by gpt-6-astra.
grounds type-stats --causesexplains unresolved calls: a missing jar (fixed bygrounds classpath), an unknown receiver, or a gap in grounds.
Error findings checked
All 133 error-level findings on the 21-project corpus were checked against the code: 69 real bugs, 58 deliberate (nearly all in test code), 5 false positives. Fixed:
rounding-of-integer-division:Math.round((double) count)is no longer reported unless an integer division is inside.weak-tls-protocol:SSLContext.getInstance("SSL")is not reported; it negotiates TLS 1.2/1.3.ignored-return-value: a discarded call that validates its argument (now.atZone(ZoneId.of(id))) is not reported, and neither is AssertJ navigation that asserts (singleElement()).null-passed-to-nonnull-parameter: not reported insideassertThrows/assertThatThrownByexecutables ortry { ...; fail(); } catch.assertj-assertion-without-check:assertThat(condition);now says to add.isTrue().
Test code
Security rules and optional-get-without-check no longer run on test sources: test servers use self-signed certificates and weak ciphers on purpose, and an empty Optional in a test just fails it. About 320 fewer findings on the corpus. Bug and testing rules still run on tests.
grounds 0.0.6
Parser and null-check release. Still experimental. The binary is for Apple Silicon, macOS 11+; on other platforms, build from source or use the Nix flake.
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.6/grounds-0.0.6-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.6-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
New parser: tree-sitter-java-orchard
Upstream tree-sitter-java is no longer maintained. grounds now uses tree-sitter-java-orchard, the fork its contributors maintain.
- Java 22–25 syntax parses: statements before
super(...)/this(...), andimport module java.base;. - Qualified record patterns (
case Shape.Circle(var r)),finaland_in patterns, and annotated varargs parse natively. About 240 lines of source-rewriting workarounds are gone. - Module imports resolve: names come from the module's exported packages and those it re-exports (
requires transitive), using a table of all JDK 25 modules. A name exported by two of those packages stays ambiguous, as javac treats it, and the file's own imports take precedence.unused-importno longer reports, or offers to delete, a module import. - Tested on the 21-project corpus (identical findings except one false positive fixed), all 15,356 files of the JDK 25 standard library (no parse errors), and a Java 8–25 syntax suite. Three review rounds by gpt-6-astra.
- Baselines keep working. For methods with qualified-type varargs parameters, fingerprints now include the type. The previous fingerprint is still accepted, so existing baselines continue to suppress those findings.
Test code
Style rules and null-dereference no longer run on test sources: tests are there to run, and a null pointer in a test just fails it. About 6,700 fewer findings on the corpus.
Null checks
- Helpers that always throw end the path. Examples:
Asserts.fail(...), methods whose body ends in athrow(static, private or final ones),@Contract("-> fail"), and JUnit/TestNG/AssertJfail(...). - Throw-on-false checks act as guards:
- Spring
Assert.state/isTrue, GuavacheckArgument/checkState, commonsValidate.isTrue, JUnitassertTrue; @Contract("false -> fail");- project helpers that start with
if (!ok) throw.
- Spring
assertThat(x).as("...").isNotNull()counts as a guard.- Copies taken before a check share its result:
ref = (T) node; if (node == null) return;.
Other fixes
inner-class-could-be-staticskips JUnit@Nestedclasses (940 false positives on the corpus).- A static
equals(Object)no longer counts as overridingequals.
The on-disk cache format changed, so the first run rebuilds it.
grounds 0.0.5
Warning-precision release. Still experimental. The binary is for Apple Silicon, macOS 11+; on other platforms, build from source or use the Nix flake.
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.5/grounds-0.0.5-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.5-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
Every warning-level rule with at least ten findings on the 21-project corpus (62 rules) was triaged on samples, and the fixes were reviewed by gpt-6-astra. About 1,500 warnings are gone, for example thingsboard 1,902 → 1,155 and camunda 2,284 → 1,478. Every fix has a regression fixture.
Dataflow
- Null checks: ternary guards (
m == null ? new HashMap<>() : m), null checks kept in a boolean (boolean both = a != null && b != null), and AssertJassertThat(x).isNotNull()(notSoftAssertions). 357 falsenull-dereferencereports removed. Optional.get(): assertion guards (assertThat(opt).isPresent(),assertTrue(opt.isPresent())) and Optionals checked before a lambda captures them. 600 false reports removed.- Resources:
close()inside its own try/catch, null-guardedclose()infinally, and JDBC statements and result sets owned by a try-with-resources connection. - Exceptions thrown inside a try-with-resources no longer skip an enclosing
finally. - A switch whose cases all return or throw no longer counts as falling through.
Rules
Precision fixes in about 30 rules, for example:
@TransactionalEventListeneris no longer treated as a proxied method;- Mockito
verify(...)is no longer an ignored result; - request-side cookies are no longer reported as insecure;
- header and config-key names are no longer reported as passwords;
- SQL built from constants is no longer reported as SQL injection;
- the
intoverflow check is aware of value bounds; - JUnit 4 message-first assertions are handled;
- test helpers are followed through calls within the same class.
15 rules that were usually right but rarely worth acting on are now info instead of warning: float-equality, system-exit, octal-literal, empty-catch-block, random-used-once, class-name-comparison, identical-operands, csrf-protection-disabled, weak-hash-algorithm, constructor-calls-overridable-method, class-shadows-supertype-name, boolean-method-returns-null, indexof-positive-check, record-array-component and odd-check-remainder.
grounds 0.0.4
Precision release from dogfooding on 21 projects and a comparison with PMD. Still experimental. The binary is for Apple Silicon, macOS 11+; on other platforms, build from source or use the Nix flake.
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.4/grounds-0.0.4-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.4-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
New
scripts/corpus.sh: a regression corpus of 21 open-source projects pinned to fixed commits. It fails on any parse error, on any compile-break or DI finding, or when a planted bug is no longer reported. All other changes show up as a diff against a baseline.scripts/pmd_compare.py: runs PMD 7 with the rules grounds cites and shows per rule what both find, what only grounds finds, and what only PMD finds.grounds check --root R pathnow resolves relative paths againstR. A missing path is an error, where before it silently analyzed nothing.
Fixed since 0.0.3
builder.countread from an enclosing class was resolved as the outercount. That caused 1,524 false "unused private field" reports on the corpus.- Deeply nested generic calls (Hamcrest matcher trees) made type inference exponential, and one helidon test hung. It now takes milliseconds.
- Error-level false positives found by reviewing every error on the eight newest projects (273 in 12 patterns). Examples:
- custom
@...Mappingannotations; - lambda arguments that select another overload;
equalscontract tests;List.of(byte[]);- AssertJ checks followed by
describedAs; - local-class field initializers.
- custom
test-without-assertionaccepts harness helpers inherited from a test base class, such as OpenRewrite'srewriteRun(about 10,800 fewer findings). Same-class asserting helpers are recognized; this check had never run before.- Imports used only in Javadoc links that wrap lines or name parameter types are no longer reported as unused.
- Recall and precision fixes in:
lost-stack-trace,catch-throwable,literal-first-in-comparison;private-constructor-class-not-final,utility-class-instantiable(it no longer suggests a private constructor for a class some file instantiates);- the naming rules,
useless-override,reference-equality-value-type.
- Corpus hardening:
- parsing of
finalin patterns and//inside text blocks; - a stack overflow on raw supertypes;
- Lombok
@FieldDefaults(makeFinal),@Builder.Defaultand@WithonisX; - Spring conditional scan roots, beans registered by
BeanDefinitionRegistryPostProcessor, and Mockito mock beans.
- parsing of
- Eight rules cited the wrong PMD or Sonar rule; two used deprecated PMD names.
- Reviewed in four rounds by gpt-6-astra. Every finding has a regression fixture.
The on-disk cache format changed (version 25), so the first run rebuilds it.
grounds 0.0.3
Parser and precision release. Still experimental; Apple Silicon macOS 11+ binary (other platforms: build from source or use the Nix flake).
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.3/grounds-0.0.3-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.3-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
Fixed since 0.0.2
- Java 22 unnamed variables and patterns (
case Boolean _, Byte _ ->,catch (E _),(_, b) ->), which tree-sitter-java cannot parse, are now handled. grounds indexreports how many files still have parse errors (0 on all 13 test codebases).- Findings that overlap code the parser could not read are dropped; well-formed code elsewhere in the file is still checked.
- A review of the 0.0.2 changes (gpt-6-astra) found 10 regressions, all fixed with tests:
- the parser workarounds could rewrite code inside strings, comments and Unicode identifiers (one case silently lost every finding in a file);
- a plain class with a leftover
@Beanmethod, or anautowireCandidate = falsefactory, could hide a genuinely missing bean; - two project classes with the same simple name were treated as classpath skew;
- inherited
@Beanmethods: overloads, and generic return types (Spring matches them by erasure); - CRLF line endings in
META-INF/spring.factories.
- Naming rules are Unicode-aware.
grounds 0.0.2
Precision release. Still experimental; Apple Silicon macOS 11+ binary (other platforms: build from source or use the Nix flake).
curl -fsSL https://github.com/mihaidaniel34/grounds/releases/download/v0.0.2/grounds-0.0.2-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv grounds-0.0.2-aarch64-apple-darwin/grounds /usr/local/bin/Not notarized: after a browser download, xattr -d com.apple.quarantine grounds.
Fixed since 0.0.1
- Sealed interfaces with nested records in pattern switches:
case Shape.Circle(double r)was reported as "Shapehas no methodCircle" (a tree-sitter-java parse bug, worked around);varcomponents of record patterns are now typed. - Eight newer Spring codebases (halo, eladmin, mall, start.spring.io, buckpal, realworld, thingsboard, dolphinscheduler) went from over 1,400 false compile-break/DI errors to zero:
- Lombok:
@SuperBuilderacross files,@UtilityClass,@Builderon constructors/methods,@Builder.Default, static factories,lombok.config(fluent/chained accessors, prefixes, copyable annotations) - classpath skew (cached Boot 3 /
jakartajars vs a Boot 2 /javaxproject) no longer proves a mismatch - Spring: MyBatis
@MapperScan, composed conditional annotations, prototype beans, inherited@Beanmethods, configurations from missing jars, same-class@Beanoverrides - imports of missing classes no longer fall back to same-named classes; overloads with unknown parameter types stay ambiguous
- Lombok:
- 24 rule false positives found by a review with gpt-6-astra (e.g.
ArrayList.equals(LinkedList),LocalDate.parsefor validation,assertThatThrownBy), plus ReactorStepVerifier.verify(), HamcrestassertThat grounds --version