Skip to content

fix(analyzer): Match raw and wildcard generics as any type#118

Merged
misonijnik merged 6 commits intomainfrom
misonijnik/generic-pattern-not
May 7, 2026
Merged

fix(analyzer): Match raw and wildcard generics as any type#118
misonijnik merged 6 commits intomainfrom
misonijnik/generic-pattern-not

Conversation

@misonijnik
Copy link
Copy Markdown
Member

@misonijnik misonijnik commented May 6, 2026

  • Treat raw class uses and unbounded wildcards (<?>) as "any type" in
    generic type-argument matching: any pattern matcher accepts them
    because the unknown type could be whatever the pattern requires.
    Net effect — Foo<?>, Foo<$T>, and raw Foo all match the same
    set of types; concrete patterns like Foo<Object> and Foo<String>
    match <Object>/<String>, <?>, and raw, but still reject other
    concrete type arguments.
  • Emit a return-type IsType condition for method-declaration
    predicates whose return type differs from the unified positive
    signature, so pattern-not-inside Foo<X> $METHOD(...) actually
    filters by return type instead of dropping the constraint.
  • Centralize the wildcard predicate as JIRType.isAnyTypeArg() in
    configuration-rules-jvm (next to erasedName()), shared by both
    runtime matchers (SerializedTypeMatching and JIRBasicAtomEvaluator).

@misonijnik misonijnik requested a review from Saloed May 6, 2026 05:39
@misonijnik misonijnik changed the title fix(analyzer): Distinguish raw vs parameterized class types in pattern matching fix(analyzer): Match raw and wildcard generics as any type May 7, 2026
@misonijnik misonijnik force-pushed the misonijnik/generic-pattern-not branch from 71b78b3 to 2cdf452 Compare May 7, 2026 12:14
misonijnik and others added 6 commits May 7, 2026 15:57
…n matching

A pattern-inside `ResponseEntity` combined with a pattern-not-inside
`ResponseEntity<$T>` was collapsing to an always-false condition: the
negative method-decl predicate dropped its returnType when its signature
differed from the unified positive signature, and JacoDB-resolved raw
class types (whose `typeArguments` are the class's own declared type
parameters) satisfied a parameterized `ResponseEntity<$T>` matcher.

Add the missing return-type IsType clause for predicates that retain a
distinct returnType, and treat a JIRClassType whose typeArguments are
exactly its declared typeParameters as raw so a parameterized matcher
no longer matches it.
…ching

Generalize generic-type pattern matching so raw class uses, unbounded
wildcards `<?>`, and unconstrained metavars `<$T>` all denote the same
set of types — "any parameterization". A concrete pattern arg like
`<String>` therefore matches raw `Foo` and `Foo<?>` (the unknown could
be `String`), while still rejecting other concrete args like
`<Integer>`.

Concretely, the runtime matchers short-circuit when the actual type
argument is a `JIRTypeVariable` (raw class form) or `JIRUnboundWildcard`
(`<?>`), and the rule converter collapses an all-wildcard parameter list
to a no-args constraint so `Foo<?>` and raw `Foo` are indistinguishable
downstream. The previously-introduced raw-vs-parameterized rejection is
removed.

Tests: add A25 (`<Object>` pattern) and A26 (`<String>` pattern)
covering the cross-product against `<concrete>`, `<?>`, raw, and other
concrete types; flip A2's raw case from Negative to Positive; drop A24
(its raw-vs-`<$T>` discrimination premise no longer exists).
Extract the "type arg denotes any type" predicate (raw-class type
variable or unbounded wildcard) to a single `JIRType.isAnyTypeArg()`
extension in `configuration-rules-jvm`, alongside the existing
`erasedName()`. Both runtime matchers now share this helper instead of
inlining the same `JIRTypeVariable || JIRUnboundWildcard` check with
duplicated comments.

Drop the conversion-time all-wildcard collapse in `typeArgsMatcher`:
it was redundant with the runtime short-circuit that already makes
`Foo<?>` and raw `Foo` match the same set of types.
…pattern

Add A27 — pattern-inside `ResponseEntity<?>` against:
- `ResponseEntity<List<String>>` and `ResponseEntity<Map<String,
Integer>>`
  (Positive) to lock in nested-generic acceptance,
- `List<String>` and `String` returns (Negative) to lock in that the
  wildcard only loosens the type-argument slot, not the class portion.
…ure negatives

Add A28 — pattern-inside `ResponseEntity<?>` combined with
pattern-not-inside `ResponseEntity<Integer>` (same parameter shape,
different return type). Methods returning `<String>` and `<Object>`
must fire as Positives; the `<Integer>` return is excluded.

Verified by temporarily reverting the return-type `IsType` emission in
`evaluateMethodSignatureCondition`: without it, the negative predicate
drops its return type, matches every method sharing the parameter
shape, and masks both Positives — exactly the regression A28 guards.
@Saloed Saloed force-pushed the misonijnik/generic-pattern-not branch from e99a2de to 73ae5bd Compare May 7, 2026 12:57
@misonijnik misonijnik merged commit 5236e63 into main May 7, 2026
6 checks passed
@misonijnik misonijnik deleted the misonijnik/generic-pattern-not branch May 7, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants