diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index e0c670c564..db55673fa1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -182,7 +182,8 @@ export type LoggerEvent = | CompileDiagnosticEvent | CompileSkipEvent | PipelineErrorEvent - | TimingEvent; + | TimingEvent + | AutoDepsDecorations; export type CompileErrorEvent = { kind: 'CompileError'; @@ -219,6 +220,11 @@ export type TimingEvent = { kind: 'Timing'; measurement: PerformanceMeasure; }; +export type AutoDepsDecorations = { + kind: 'AutoDepsDecorations'; + useEffectCallExpr: t.SourceLocation | null; + decorations: Array; +}; export type Logger = { logEvent: (filename: string | null, event: LoggerEvent) => void; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 0d27ac7ca0..e14b1af115 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -188,6 +188,7 @@ export function inferEffectDependencies(fn: HIRFunction): void { * the `infer-effect-deps/pruned-nonreactive-obj` fixture for an * explanation. */ + const usedDeps = []; for (const dep of scopeInfo.deps) { if ( ((isUseRefType(dep.identifier) || @@ -207,8 +208,19 @@ export function inferEffectDependencies(fn: HIRFunction): void { ); newInstructions.push(...instructions); 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({ id: makeInstructionId(0), loc: GeneratedSource, @@ -340,3 +352,31 @@ function inferReactiveIdentifiers(fn: HIRFunction): Set { } return reactiveIds; } + +function collectDepUsages( + deps: Array, + fnExpr: FunctionExpression, +): Array { + const identifiers: Map = new Map(); + const loadedDeps: Set = 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; +}