mirror of
https://github.com/morluto/rea.git
synced 2026-10-29 02:45:58 +00:00
fix(javascript): preserve lexical binding and default export capture semantics
Resolve CommonJS wrapper bindings from the parser-owned source type. Capture default identifier export values at the export statement while preserving live named bindings. Refs: #3261, #3297 Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
This commit is contained in:
1 parent
322506d0c0
commit
dae1e55381
6 files changed
+111
-16
No files matched your search
@@ -28,6 +28,7 @@ import { evaluateSemanticExpression } from "./javascriptSemanticValues.js";
|
||||
import { range } from "./javascriptStaticAnalysisHelpers.js";
|
||||
import { semanticReturnCoverage } from "./javascriptSemanticCoverage.js";
|
||||
import { semanticCallableResultExpressions } from "./javascriptSemanticCallableResults.js";
|
||||
import { semanticReadInitializers } from "./javascriptSemanticMutationInitializers.js";
|
||||
|
||||
interface ReturnExpression {
|
||||
readonly node: t.Node | null;
|
||||
@@ -104,15 +105,18 @@ export const resolveSemanticModuleCallables = (
|
||||
if (link.kind === "type-import") return { ...link, callableId: null };
|
||||
// Named exports follow their lexical binding, including every assignment.
|
||||
// A declaration's ID or a callable's display name cannot prove that link.
|
||||
if (link.localName === null)
|
||||
const captured = state.defaultExportExpressions.get(link);
|
||||
if (link.localName === null && captured === undefined)
|
||||
return link.callableId !== null && callableIds.has(link.callableId)
|
||||
? link
|
||||
: { ...link, callableId: null };
|
||||
const binding = state.moduleLinkBindings.get(link);
|
||||
const resolved =
|
||||
binding === undefined
|
||||
? []
|
||||
: callableIdsForBinding(binding, state, callableIds, new Set());
|
||||
captured !== undefined
|
||||
? callableIdsForNode(captured, state, callableIds, new Set(), true)
|
||||
: binding === undefined
|
||||
? []
|
||||
: callableIdsForBinding(binding, state, callableIds, new Set());
|
||||
const provenance =
|
||||
binding === undefined || link.specifier !== null
|
||||
? undefined
|
||||
@@ -139,21 +143,29 @@ const callableIdsForBinding = (
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
admitted: ReadonlySet<string>,
|
||||
seen: ReadonlySet<string>,
|
||||
read?: t.Node,
|
||||
): string[] => {
|
||||
// A single initializer does not prove an assignment executes or replace
|
||||
// the unknown incoming value of a parameter/catch binding.
|
||||
if (
|
||||
seen.has(binding.bindingId) ||
|
||||
binding.initializers.length !== 1 ||
|
||||
(read === undefined && binding.initializers.length !== 1) ||
|
||||
binding.definitions.some(
|
||||
({ kind }) =>
|
||||
kind === "assignment" || kind === "parameter" || kind === "catch",
|
||||
(read === undefined && kind === "assignment") ||
|
||||
kind === "parameter" ||
|
||||
kind === "catch",
|
||||
)
|
||||
)
|
||||
return [];
|
||||
const initializer = binding.initializers[0];
|
||||
const selected =
|
||||
read === undefined
|
||||
? binding
|
||||
: semanticReadInitializers(binding, read, state.parentsByNode);
|
||||
const initializer = selected.initializers[0];
|
||||
if (
|
||||
initializer === undefined ||
|
||||
selected.initializers.length !== 1 ||
|
||||
initializer.projection.length > 0 ||
|
||||
state.conditionallySkippedExpressions.has(initializer.node)
|
||||
)
|
||||
@@ -163,6 +175,7 @@ const callableIdsForBinding = (
|
||||
state,
|
||||
admitted,
|
||||
new Set([...seen, binding.bindingId]),
|
||||
read !== undefined,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -171,17 +184,24 @@ const callableIdsForNode = (
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
admitted: ReadonlySet<string>,
|
||||
seen: ReadonlySet<string>,
|
||||
snapshot = false,
|
||||
): string[] => {
|
||||
const unwrapped = unwrapJavaScriptExpression(node).node;
|
||||
if (unwrapped !== node)
|
||||
return callableIdsForNode(unwrapped, state, admitted, seen);
|
||||
return callableIdsForNode(unwrapped, state, admitted, seen, snapshot);
|
||||
const direct = semanticCallableIdForNode(node);
|
||||
if (direct !== null && admitted.has(direct)) return [direct];
|
||||
if (t.isIdentifier(node)) {
|
||||
const binding = resolveSemanticBindingState(state, node, node.name);
|
||||
return binding === undefined
|
||||
? []
|
||||
: callableIdsForBinding(binding, state, admitted, seen);
|
||||
: callableIdsForBinding(
|
||||
binding,
|
||||
state,
|
||||
admitted,
|
||||
seen,
|
||||
snapshot ? node : undefined,
|
||||
);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user