[forgive] Emit AutoDepsDecoration event when inferring effect deps (#32997)

Emits a new event for decorating inferred effect dependencies.

Co-authored-by: Jordan Brown <jmbrown@meta.com>
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32997).
* #33002
* #33001
* #33000
* #32999
* #32998
* __->__ #32997
* #32996

---------

Co-authored-by: Jordan Brown <jmbrown@meta.com>
This commit is contained in:
lauren 2025-04-23 20:51:38 -04:00 committed by GitHub
parent 71d0896a4a
commit cd7d236682
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 1 deletions

View File

@ -182,7 +182,8 @@ export type LoggerEvent =
| CompileDiagnosticEvent | CompileDiagnosticEvent
| CompileSkipEvent | CompileSkipEvent
| PipelineErrorEvent | PipelineErrorEvent
| TimingEvent; | TimingEvent
| AutoDepsDecorations;
export type CompileErrorEvent = { export type CompileErrorEvent = {
kind: 'CompileError'; kind: 'CompileError';
@ -219,6 +220,11 @@ export type TimingEvent = {
kind: 'Timing'; kind: 'Timing';
measurement: PerformanceMeasure; measurement: PerformanceMeasure;
}; };
export type AutoDepsDecorations = {
kind: 'AutoDepsDecorations';
useEffectCallExpr: t.SourceLocation | null;
decorations: Array<t.SourceLocation | null>;
};
export type Logger = { export type Logger = {
logEvent: (filename: string | null, event: LoggerEvent) => void; logEvent: (filename: string | null, event: LoggerEvent) => void;

View File

@ -188,6 +188,7 @@ export function inferEffectDependencies(fn: HIRFunction): void {
* the `infer-effect-deps/pruned-nonreactive-obj` fixture for an * the `infer-effect-deps/pruned-nonreactive-obj` fixture for an
* explanation. * explanation.
*/ */
const usedDeps = [];
for (const dep of scopeInfo.deps) { for (const dep of scopeInfo.deps) {
if ( if (
((isUseRefType(dep.identifier) || ((isUseRefType(dep.identifier) ||
@ -207,8 +208,19 @@ export function inferEffectDependencies(fn: HIRFunction): void {
); );
newInstructions.push(...instructions); newInstructions.push(...instructions);
effectDeps.push(place); effectDeps.push(place);
usedDeps.push(dep);
} }
// For LSP autodeps feature.
fn.env.logger?.logEvent(fn.env.filename, {
kind: 'AutoDepsDecorations',
useEffectCallExpr:
typeof value.loc !== 'symbol' ? value.loc : null,
decorations: collectDepUsages(usedDeps, fnExpr.value).map(loc =>
typeof loc !== 'symbol' ? loc : null,
),
});
newInstructions.push({ newInstructions.push({
id: makeInstructionId(0), id: makeInstructionId(0),
loc: GeneratedSource, loc: GeneratedSource,
@ -340,3 +352,31 @@ function inferReactiveIdentifiers(fn: HIRFunction): Set<IdentifierId> {
} }
return reactiveIds; return reactiveIds;
} }
function collectDepUsages(
deps: Array<ReactiveScopeDependency>,
fnExpr: FunctionExpression,
): Array<SourceLocation> {
const identifiers: Map<IdentifierId, ReactiveScopeDependency> = new Map();
const loadedDeps: Set<IdentifierId> = new Set();
const sourceLocations = [];
for (const dep of deps) {
identifiers.set(dep.identifier.id, dep);
}
for (const [, block] of fnExpr.loweredFunc.func.body.blocks) {
for (const instr of block.instructions) {
if (instr.value.kind === 'LoadLocal') {
loadedDeps.add(instr.lvalue.identifier.id);
}
for (const place of eachInstructionOperand(instr)) {
if (loadedDeps.has(place.identifier.id)) {
// TODO(@jbrown215): handle member exprs!!
sourceLocations.push(place.identifier.loc);
}
}
}
}
return sourceLocations;
}