Skip to content

Releases: mihaidaniel34/grounds

grounds 0.0.11

Choose a tag to compare

@github-actions github-actions released this 16 Oct 10:23
ca22f56

Release notes follow.

grounds 0.0.10

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 15 Oct 11:48
a706656

What's Changed

Full Changelog: v0.0.9...v0.0.10

grounds 0.0.9

Choose a tag to compare

@github-actions github-actions released this 15 Oct 06:18
ee7a75b

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 null calls;
  • 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 still String(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() and getClass() as missing (225 false errors on camunda).
  • unresolved-method: an array passed alone in a varargs position counts as one element (of(byte[]) for of(byte[]...)).
  • DI: Jakarta Data repositories are beans; wiring that involves a class defined in several source files is left unknown; ObjectProvider / Provider with 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

Choose a tag to compare

@github-actions github-actions released this 11 Oct 20:41

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-files finding 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 named build, lost those sources: apollo's com.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-method quotes the missing methods in its message.

grounds 0.0.7

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 11 Oct 18:35

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 --causes explains unresolved calls: a missing jar (fixed by grounds 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 inside assertThrows/assertThatThrownBy executables or try { ...; 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

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 11 Oct 09:42

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(...), and import module java.base;.
  • Qualified record patterns (case Shape.Circle(var r)), final and _ 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-import no 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 a throw (static, private or final ones), @Contract("-> fail"), and JUnit/TestNG/AssertJ fail(...).
  • Throw-on-false checks act as guards:
    • Spring Assert.state/isTrue, Guava checkArgument/checkState, commons Validate.isTrue, JUnit assertTrue;
    • @Contract("false -> fail");
    • project helpers that start with if (!ok) throw.
  • 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-static skips JUnit @Nested classes (940 false positives on the corpus).
  • A static equals(Object) no longer counts as overriding equals.

The on-disk cache format changed, so the first run rebuilds it.

grounds 0.0.5

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 11 Oct 08:13

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 AssertJ assertThat(x).isNotNull() (not SoftAssertions). 357 false null-dereference reports 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-guarded close() in finally, 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:

  • @TransactionalEventListener is 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 int overflow 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

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 11 Oct 07:07

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 path now resolves relative paths against R. A missing path is an error, where before it silently analyzed nothing.

Fixed since 0.0.3

  • builder.count read from an enclosing class was resolved as the outer count. 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 @...Mapping annotations;
    • lambda arguments that select another overload;
    • equals contract tests;
    • List.of(byte[]);
    • AssertJ checks followed by describedAs;
    • local-class field initializers.
  • test-without-assertion accepts harness helpers inherited from a test base class, such as OpenRewrite's rewriteRun (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 final in patterns and // inside text blocks;
    • a stack overflow on raw supertypes;
    • Lombok @FieldDefaults(makeFinal), @Builder.Default and @With on isX;
    • Spring conditional scan roots, beans registered by BeanDefinitionRegistryPostProcessor, and Mockito mock beans.
  • 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

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 10 Oct 16:46

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 index reports 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 @Bean method, or an autowireCandidate = false factory, could hide a genuinely missing bean;
    • two project classes with the same simple name were treated as classpath skew;
    • inherited @Bean methods: 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

Choose a tag to compare

@mihaidaniel34 mihaidaniel34 released this 10 Oct 14:31

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 "Shape has no method Circle" (a tree-sitter-java parse bug, worked around); var components 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: @SuperBuilder across files, @UtilityClass, @Builder on constructors/methods, @Builder.Default, static factories, lombok.config (fluent/chained accessors, prefixes, copyable annotations)
    • classpath skew (cached Boot 3 / jakarta jars vs a Boot 2 / javax project) no longer proves a mismatch
    • Spring: MyBatis @MapperScan, composed conditional annotations, prototype beans, inherited @Bean methods, configurations from missing jars, same-class @Bean overrides
    • imports of missing classes no longer fall back to same-named classes; overloads with unknown parameter types stay ambiguous
  • 24 rule false positives found by a review with gpt-6-astra (e.g. ArrayList.equals(LinkedList), LocalDate.parse for validation, assertThatThrownBy), plus Reactor StepVerifier.verify(), Hamcrest assertThat
  • grounds --version