diff --git a/graph/test/java/expected/61-var-local-type.known-missing b/graph/test/java/expected/61-var-local-type.known-missing new file mode 100644 index 000000000..996b5ec5e --- /dev/null +++ b/graph/test/java/expected/61-var-local-type.known-missing @@ -0,0 +1,13 @@ +# Accepted gap, measured by the bytecode oracle in CI: a `var` local whose type comes from something +# other than its initializer expression is left a declared unknown. Before #1401 each of these was a +# wrong `external:var.` edge; now the receiver is honestly untyped. Three shapes: +# - the element variable of an enhanced for loop (`for (var w : widgets)`): its type is the +# iterable's element type; +# - a try-with-resources variable (`try (var w = open())` where open() is not typed client-side); +# - `var` lambda parameters (`(var w) -> w.paint()`): the type comes from the target functional +# interface. +# Follow-up: type `var` in these three shapes. It cannot rot: when one starts resolving, this line +# reports NOW-FIXED and the case fails until it is removed. +probe.VarLocals#fromEnhancedFor -> probe.Widget#paint() +probe.VarLocals#fromResource -> probe.Widget#close() +probe.VarLocals#lambdaParams -> probe.Widget#paint() diff --git a/graph/test/java/expected/61-var-local-type.oracle b/graph/test/java/expected/61-var-local-type.oracle new file mode 100644 index 000000000..b29608363 --- /dev/null +++ b/graph/test/java/expected/61-var-local-type.oracle @@ -0,0 +1,4 @@ +oracle=20 engine=17 agree=17 missing=3 (known 3, NEW 0) extra=0 + known-missing probe.VarLocals#fromEnhancedFor -> probe.Widget#paint() + known-missing probe.VarLocals#fromResource -> probe.Widget#close() + known-missing probe.VarLocals#lambdaParams -> probe.Widget#paint() diff --git a/graph/test/java/expected/oracle-agreement.txt b/graph/test/java/expected/oracle-agreement.txt index a204958ea..22a372c44 100644 --- a/graph/test/java/expected/oracle-agreement.txt +++ b/graph/test/java/expected/oracle-agreement.txt @@ -1,4 +1,4 @@ -cases compared 45 agreeing 45 +cases compared 46 agreeing 46 CONSTRUCTOR-RULE disagreements: 0 cases, 0 rows OTHER disagreements: 0 rows naming no method: 0 diff --git a/graph/test/typescript/cases/75-jsx-wrapper-guards/src/app.tsx b/graph/test/typescript/cases/75-jsx-wrapper-guards/src/app.tsx new file mode 100644 index 000000000..9a7c373c2 --- /dev/null +++ b/graph/test/typescript/cases/75-jsx-wrapper-guards/src/app.tsx @@ -0,0 +1,73 @@ +import { dynamic, lazy, makeThing, memo, observer, useMemoLike, type Fc } from "./parts"; +import { Button } from "./ui/Button"; + +export function factoryArgBody(): number { + return 3; +} +export function memoBody(): number { + return 4; +} +function ObservedImpl(): string { + return "observed"; +} + +// A loader that projects a NAMED export: the component is NamedHeavy, never Heavy. +const LazyNamed = lazy(() => import("./heavy").then((m) => ({ default: m.NamedHeavy }))); +const DynNamed = dynamic(() => import("./heavy").then((m) => m.NamedHeavy)); +// …and the same projections written with a block body, where the returned value is a row. +const LazyBlock = lazy(() => import("./heavy").then((m) => { return { default: m.NamedHeavy }; })); +const DynAwait = dynamic(async () => { const m = await import("./heavy"); return m.NamedHeavy; }); +// CONTROL: a loader returning the module itself renders its default export. +const LazyDefault = lazy(() => import("./heavy")); +const DynDefault = dynamic(() => import("./heavy")); + +// NOT wrappers: argument 0 is not what the product renders. +const Thing = makeThing(() => factoryArgBody()); +const Made = useMemoLike(() => function MadeInner(): string { return "made"; }, []); + +// CONTROL: real wrappers still reach their argument. +const Memoed = memo(function MemoInner(): string { return String(memoBody()); }); +const Observed = observer(ObservedImpl); + +export function Page(): string { + return ( +
+ + + + + + + + + + +
+ ); +} + +// A destructured prop SHADOWS the imported Button: none of these render ui/Button. +export function ShorthandTag({ Button }: { Button: Fc<{}> }): string { + return